WIP reproduccion de audios en feeds y push notifications
This commit is contained in:
parent
c87e12caab
commit
0aa7b1001f
@ -23,7 +23,7 @@ android {
|
|||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "0.1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,21 @@ package com.isolaatti
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import android.util.Log
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import com.google.firebase.Firebase
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.isolaatti.connectivity.ConnectivityCallbackImpl
|
import com.isolaatti.connectivity.ConnectivityCallbackImpl
|
||||||
|
import com.isolaatti.push_notifications.FcmService
|
||||||
|
import com.isolaatti.push_notifications.PushNotificationsApi
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import retrofit2.awaitResponse
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
@ -16,8 +26,12 @@ class MyApplication : Application() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var myApp: MyApplication
|
lateinit var myApp: MyApplication
|
||||||
|
const val LOG_TAG = "MyApplication"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var pushNotificationsApi: PushNotificationsApi
|
||||||
|
|
||||||
private val activityLifecycleCallbacks = ActivityLifecycleCallbacks()
|
private val activityLifecycleCallbacks = ActivityLifecycleCallbacks()
|
||||||
lateinit var connectivityCallbackImpl: ConnectivityCallbackImpl
|
lateinit var connectivityCallbackImpl: ConnectivityCallbackImpl
|
||||||
|
|
||||||
@ -27,6 +41,19 @@ class MyApplication : Application() {
|
|||||||
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
|
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
|
||||||
connectivityCallbackImpl = ConnectivityCallbackImpl()
|
connectivityCallbackImpl = ConnectivityCallbackImpl()
|
||||||
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(connectivityCallbackImpl)
|
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(connectivityCallbackImpl)
|
||||||
|
|
||||||
|
FirebaseMessaging.getInstance().token.addOnCompleteListener {
|
||||||
|
if(!it.isSuccessful) {
|
||||||
|
Log.w(LOG_TAG, "Failed fetching fcm token")
|
||||||
|
return@addOnCompleteListener
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val response = pushNotificationsApi.registerDevice(it.result).awaitResponse()
|
||||||
|
Log.d(FcmService.LOG_TAG, "Device registered. FCM token: $it.result")
|
||||||
|
Log.d(FcmService.LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTerminate() {
|
override fun onTerminate() {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.isolaatti.audio.common.data
|
package com.isolaatti.audio.common.data
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
data class AudiosDto(val data: List<AudioDto>)
|
data class AudiosDto(val data: List<AudioDto>)
|
||||||
@ -10,4 +11,4 @@ data class AudioDto(
|
|||||||
val userId: Int,
|
val userId: Int,
|
||||||
val firestoreObjectPath: String,
|
val firestoreObjectPath: String,
|
||||||
val userName: String
|
val userName: String
|
||||||
)
|
): Serializable
|
||||||
@ -6,6 +6,8 @@ abstract class Playable {
|
|||||||
var isPlaying: Boolean = false
|
var isPlaying: Boolean = false
|
||||||
abstract val uri: Uri
|
abstract val uri: Uri
|
||||||
var isLoading: Boolean = false
|
var isLoading: Boolean = false
|
||||||
|
var progress: Int = 0
|
||||||
|
var duration: Int = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image url, null indicating no image should be shown
|
* Image url, null indicating no image should be shown
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package com.isolaatti.common
|
package com.isolaatti.common
|
||||||
|
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
|
||||||
interface OnUserInteractedWithPostCallback : OnUserInteractedCallback {
|
interface OnUserInteractedWithPostCallback : OnUserInteractedCallback {
|
||||||
fun onLiked(postId: Long)
|
fun onLiked(postId: Long)
|
||||||
fun onUnLiked(postId: Long)
|
fun onUnLiked(postId: Long)
|
||||||
fun onComment(postId: Long)
|
fun onComment(postId: Long)
|
||||||
fun onOpenPost(postId: Long)
|
fun onOpenPost(postId: Long)
|
||||||
|
fun onPlay(audio: Audio)
|
||||||
}
|
}
|
||||||
@ -22,6 +22,9 @@ import com.google.android.material.card.MaterialCardView
|
|||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.about.AboutActivity
|
import com.isolaatti.about.AboutActivity
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
import com.isolaatti.audio.common.domain.Playable
|
||||||
|
import com.isolaatti.audio.player.AudioPlayerConnector
|
||||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||||
import com.isolaatti.common.Dialogs
|
import com.isolaatti.common.Dialogs
|
||||||
import com.isolaatti.common.ErrorMessageViewModel
|
import com.isolaatti.common.ErrorMessageViewModel
|
||||||
@ -70,6 +73,8 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
private lateinit var viewBinding: FragmentFeedBinding
|
private lateinit var viewBinding: FragmentFeedBinding
|
||||||
private lateinit var adapter: PostsRecyclerViewAdapter
|
private lateinit var adapter: PostsRecyclerViewAdapter
|
||||||
|
|
||||||
|
private lateinit var audioPlayerConnector: AudioPlayerConnector
|
||||||
|
|
||||||
// region launchers
|
// region launchers
|
||||||
|
|
||||||
private val createDiscussion = registerForActivityResult(CreatePostContract()) {
|
private val createDiscussion = registerForActivityResult(CreatePostContract()) {
|
||||||
@ -127,6 +132,34 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
private val audioPlayerConnectorListener = object: AudioPlayerConnector.Listener {
|
||||||
|
override fun onPlaying(isPlaying: Boolean, audio: Playable) {
|
||||||
|
if(audio is Audio)
|
||||||
|
adapter.setIsPlaying(isPlaying, audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLoading(isLoading: Boolean, audio: Playable) {
|
||||||
|
if(audio is Audio)
|
||||||
|
adapter.setIsLoading(isLoading, audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun progressChanged(second: Int, audio: Playable) {
|
||||||
|
if(audio is Audio)
|
||||||
|
adapter.setProgress(second, audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun durationChanged(duration: Int, audio: Playable) {
|
||||||
|
if(audio is Audio)
|
||||||
|
adapter.setDuration(duration, audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnded(audio: Playable) {
|
||||||
|
if(audio is Audio)
|
||||||
|
adapter.setEnded(audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// region events
|
// region events
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@ -149,6 +182,9 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audioPlayerConnector = AudioPlayerConnector(requireContext())
|
||||||
|
audioPlayerConnector.addListener(audioPlayerConnectorListener)
|
||||||
|
viewLifecycleOwner.lifecycle.addObserver(audioPlayerConnector)
|
||||||
|
|
||||||
val markwon = Markwon.builder(requireContext())
|
val markwon = Markwon.builder(requireContext())
|
||||||
.usePlugin(object: AbstractMarkwonPlugin() {
|
.usePlugin(object: AbstractMarkwonPlugin() {
|
||||||
@ -258,6 +294,10 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
PostViewerActivity.startActivity(requireContext(), postId)
|
PostViewerActivity.startActivity(requireContext(), postId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPlay(audio: Audio) {
|
||||||
|
audioPlayerConnector.playPauseAudio(audio)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onProfileClick(userId: Int) {
|
override fun onProfileClick(userId: Int) {
|
||||||
ProfileActivity.startActivity(requireContext(), userId)
|
ProfileActivity.startActivity(requireContext(), userId)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
package com.isolaatti.home.notifications.presentation
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
|
|
||||||
class NotificationsViewModel : ViewModel() {
|
|
||||||
// TODO: Implement the ViewModel
|
|
||||||
}
|
|
||||||
25
app/src/main/java/com/isolaatti/notifications/Module.kt
Normal file
25
app/src/main/java/com/isolaatti/notifications/Module.kt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.isolaatti.notifications
|
||||||
|
|
||||||
|
import com.isolaatti.connectivity.RetrofitClient
|
||||||
|
import com.isolaatti.notifications.data.NotificationsApi
|
||||||
|
import com.isolaatti.notifications.data.NotificationsRepositoryImpl
|
||||||
|
import com.isolaatti.notifications.domain.NotificationsRepository
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class Module {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideNotificationsApi(retrofitClient: RetrofitClient): NotificationsApi {
|
||||||
|
return retrofitClient.client.create(NotificationsApi::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideNotificationsRepository(notificationsApi: NotificationsApi): NotificationsRepository {
|
||||||
|
return NotificationsRepositoryImpl(notificationsApi)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.isolaatti.home.notifications.data
|
package com.isolaatti.notifications.data
|
||||||
|
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.isolaatti.home.notifications.data
|
package com.isolaatti.notifications.data
|
||||||
|
|
||||||
|
import com.google.gson.internal.LinkedTreeMap
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
data class NotificationsDto(
|
data class NotificationsDto(
|
||||||
@ -18,5 +19,6 @@ data class NotificationPayload(
|
|||||||
val type: String,
|
val type: String,
|
||||||
val authorId: Int,
|
val authorId: Int,
|
||||||
val authorName: String?,
|
val authorName: String?,
|
||||||
val intentData: String?
|
val intentData: String?,
|
||||||
|
val data: Map<String, String>
|
||||||
)
|
)
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.isolaatti.notifications.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.isolaatti.notifications.domain.Notification
|
||||||
|
import com.isolaatti.notifications.domain.NotificationsRepository
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import retrofit2.awaitResponse
|
||||||
|
|
||||||
|
class NotificationsRepositoryImpl(private val notificationsApi: NotificationsApi) : NotificationsRepository {
|
||||||
|
companion object {
|
||||||
|
const val LOG_TAG = "NotificationsRepositoryImpl"
|
||||||
|
}
|
||||||
|
override fun getNotifications(after: Long?): Flow<Resource<List<Notification>>> = flow {
|
||||||
|
try {
|
||||||
|
val response = notificationsApi.getNotifications(after).awaitResponse()
|
||||||
|
|
||||||
|
if(response.isSuccessful) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e(LOG_TAG, "getNotifications(): Request is not successful, response code is ${response.code()}")
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e(LOG_TAG, e.message.toString())
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.isolaatti.notifications.domain
|
||||||
|
|
||||||
|
import com.isolaatti.databinding.NotificationItemBinding
|
||||||
|
import com.isolaatti.notifications.data.NotificationDto
|
||||||
|
import com.isolaatti.notifications.data.NotificationPayload
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
|
||||||
|
class GenericNotification(id: Long, date: ZonedDateTime, userId: Int, read: Boolean) : Notification(id, date, userId, read) {
|
||||||
|
|
||||||
|
var title: String? = null
|
||||||
|
var message: String? = null
|
||||||
|
|
||||||
|
override fun ingestPayload(notificationPayload: NotificationPayload) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(notificationBinding: NotificationItemBinding) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TYPE = "generic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LikeNotification(id: Long, date: ZonedDateTime, userId: Int, read: Boolean) : Notification(id, date, userId, read) {
|
||||||
|
companion object {
|
||||||
|
const val TYPE = "like"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun ingestPayload(notificationPayload: NotificationPayload) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(notificationBinding: NotificationItemBinding) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FollowNotification(id: Long, date: ZonedDateTime, userId: Int, read: Boolean) : Notification(id, date, userId, read) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TYPE = "follow"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun ingestPayload(notificationPayload: NotificationPayload) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(notificationBinding: NotificationItemBinding) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Notification(
|
||||||
|
val id: Long,
|
||||||
|
val date: ZonedDateTime,
|
||||||
|
val userId: Int,
|
||||||
|
var read: Boolean
|
||||||
|
) {
|
||||||
|
|
||||||
|
abstract fun ingestPayload(notificationPayload: NotificationPayload)
|
||||||
|
|
||||||
|
abstract fun bind(notificationBinding: NotificationItemBinding)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromDto(notificationDto: NotificationDto): Notification? {
|
||||||
|
return when(notificationDto.payload.type) {
|
||||||
|
GenericNotification.TYPE -> {
|
||||||
|
|
||||||
|
GenericNotification(
|
||||||
|
notificationDto.id,
|
||||||
|
notificationDto.date,
|
||||||
|
notificationDto.userId,
|
||||||
|
notificationDto.read
|
||||||
|
).apply {
|
||||||
|
ingestPayload(notificationDto.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LikeNotification.TYPE -> {
|
||||||
|
LikeNotification(
|
||||||
|
notificationDto.id,
|
||||||
|
notificationDto.date,
|
||||||
|
notificationDto.userId,
|
||||||
|
notificationDto.read
|
||||||
|
).apply {
|
||||||
|
ingestPayload(notificationDto.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FollowNotification.TYPE -> {
|
||||||
|
FollowNotification(
|
||||||
|
notificationDto.id,
|
||||||
|
notificationDto.date,
|
||||||
|
notificationDto.userId,
|
||||||
|
notificationDto.read
|
||||||
|
).apply {
|
||||||
|
ingestPayload(notificationDto.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.isolaatti.notifications.domain
|
||||||
|
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface NotificationsRepository {
|
||||||
|
fun getNotifications(after: Long?): Flow<Resource<List<Notification>>>
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.isolaatti.notifications.presentation
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
|
import com.isolaatti.databinding.NotificationItemBinding
|
||||||
|
import com.isolaatti.notifications.domain.Notification
|
||||||
|
|
||||||
|
class NotificationsAdapter : ListAdapter<Notification, NotificationsAdapter.NotificationViewHolder>(
|
||||||
|
diffCallback
|
||||||
|
) {
|
||||||
|
inner class NotificationViewHolder(val notificationItemBinding: NotificationItemBinding) : ViewHolder(notificationItemBinding.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder {
|
||||||
|
return NotificationViewHolder(NotificationItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) {
|
||||||
|
getItem(position).bind(holder.notificationItemBinding)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val diffCallback = object: DiffUtil.ItemCallback<Notification>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Notification, newItem: Notification): Boolean {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Notification, newItem: Notification): Boolean {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.isolaatti.notifications.presentation
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.isolaatti.notifications.domain.NotificationsRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class NotificationsViewModel @Inject constructor(private val notificationsRepository: NotificationsRepository) : ViewModel() {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.isolaatti.home.notifications.ui
|
package com.isolaatti.notifications.ui
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -7,7 +7,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.home.notifications.presentation.NotificationsViewModel
|
import com.isolaatti.notifications.presentation.NotificationsViewModel
|
||||||
|
|
||||||
class NotificationsFragment : Fragment() {
|
class NotificationsFragment : Fragment() {
|
||||||
|
|
||||||
@ -2,6 +2,7 @@ package com.isolaatti.posting.posts.data.remote
|
|||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.isolaatti.audio.common.data.AudioDto
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
data class FeedDto(
|
data class FeedDto(
|
||||||
@ -23,7 +24,8 @@ data class FeedDto(
|
|||||||
var numberOfComments: Int,
|
var numberOfComments: Int,
|
||||||
val userName: String,
|
val userName: String,
|
||||||
val squadName: String?,
|
val squadName: String?,
|
||||||
var liked: Boolean
|
var liked: Boolean,
|
||||||
|
var audio: AudioDto?
|
||||||
): Parcelable {
|
): Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
@ -32,7 +34,8 @@ data class FeedDto(
|
|||||||
parcel.readInt(),
|
parcel.readInt(),
|
||||||
parcel.readString()!!,
|
parcel.readString()!!,
|
||||||
parcel.readString(),
|
parcel.readString(),
|
||||||
parcel.readByte() != 0.toByte()
|
parcel.readByte() != 0.toByte(),
|
||||||
|
parcel.readParcelable(AudioDto::class.java.classLoader)
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Post(
|
data class Post(
|
||||||
@ -93,6 +96,7 @@ data class FeedDto(
|
|||||||
parcel.writeString(userName)
|
parcel.writeString(userName)
|
||||||
parcel.writeString(squadName)
|
parcel.writeString(squadName)
|
||||||
parcel.writeByte(if (liked) 1 else 0)
|
parcel.writeByte(if (liked) 1 else 0)
|
||||||
|
parcel.writeSerializable(audio)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
|
|||||||
@ -54,7 +54,8 @@ data class Post(
|
|||||||
numberOfLikes = it.numberOfLikes,
|
numberOfLikes = it.numberOfLikes,
|
||||||
userName = it.userName,
|
userName = it.userName,
|
||||||
squadName = it.squadName,
|
squadName = it.squadName,
|
||||||
liked = it.liked
|
liked = it.liked,
|
||||||
|
audio = it.audio?.let { audioDto -> Audio.fromDto(audioDto) }
|
||||||
)
|
)
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,107 +7,149 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
import coil.load
|
import coil.load
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||||
import com.isolaatti.common.OnUserInteractedWithPostCallback
|
import com.isolaatti.common.OnUserInteractedWithPostCallback
|
||||||
|
import com.isolaatti.databinding.PostLayoutBinding
|
||||||
import com.isolaatti.posting.posts.domain.entity.Post
|
import com.isolaatti.posting.posts.domain.entity.Post
|
||||||
import com.isolaatti.utils.UrlGen.userProfileImage
|
import com.isolaatti.utils.UrlGen.userProfileImage
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
|
|
||||||
class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callback: OnUserInteractedWithPostCallback) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
|
class PostsRecyclerViewAdapter (
|
||||||
|
private val markwon: Markwon,
|
||||||
|
private val callback: OnUserInteractedWithPostCallback
|
||||||
|
) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
|
||||||
private var postList: List<Post>? = null
|
private var postList: List<Post>? = null
|
||||||
inner class FeedViewHolder(itemView: View) : ViewHolder(itemView) {
|
inner class FeedViewHolder(val itemBinding: PostLayoutBinding) : ViewHolder(itemBinding.root) {
|
||||||
fun bindView(postDto: Post, payloads: List<Any>) {
|
fun bindView(post: Post, payloads: List<Any>) {
|
||||||
|
|
||||||
Log.d("payloads", payloads.count().toString())
|
|
||||||
val likeButton: MaterialButton = itemView.findViewById(R.id.like_button)
|
|
||||||
val commentsButton: MaterialButton = itemView.findViewById(R.id.comment_button)
|
|
||||||
|
|
||||||
if(payloads.isNotEmpty()) {
|
if(payloads.isNotEmpty()) {
|
||||||
for(payload in payloads) {
|
for(payload in payloads) {
|
||||||
when(payload) {
|
when {
|
||||||
is LikeCountUpdatePayload -> {
|
payload is LikeCountUpdatePayload -> {
|
||||||
|
itemBinding.likeButton.isEnabled = true
|
||||||
|
|
||||||
|
if(post.liked) {
|
||||||
likeButton.isEnabled = true
|
itemBinding.likeButton.setIconTintResource(R.color.purple_lighter)
|
||||||
|
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
||||||
if(postDto.liked) {
|
|
||||||
likeButton.setIconTintResource(R.color.purple_lighter)
|
|
||||||
likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
|
||||||
} else {
|
} else {
|
||||||
likeButton.setIconTintResource(R.color.on_surface)
|
itemBinding.likeButton.setIconTintResource(R.color.on_surface)
|
||||||
likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
||||||
}
|
}
|
||||||
|
|
||||||
likeButton.text = postDto.numberOfLikes.toString()
|
itemBinding.likeButton.text = post.numberOfLikes.toString()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
payload is CommentsCountUpdatePayload -> {
|
||||||
is CommentsCountUpdatePayload -> {
|
itemBinding.commentButton.text = post.numberOfComments.toString()
|
||||||
|
}
|
||||||
commentsButton.text = postDto.numberOfComments.toString()
|
payload is AudioEventPayload && payload == AudioEventPayload.IsPLaying -> {
|
||||||
|
val audio = post.audio
|
||||||
|
if(audio != null){
|
||||||
|
itemBinding.audio.playButton.icon =
|
||||||
|
AppCompatResources.getDrawable(
|
||||||
|
itemView.context,
|
||||||
|
if(audio.isPlaying) R.drawable.baseline_pause_circle_24 else R.drawable.baseline_play_circle_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload is AudioEventPayload && payload == AudioEventPayload.ProgressChanged -> {
|
||||||
|
val audio = post.audio
|
||||||
|
if(audio != null){
|
||||||
|
itemBinding.audio.audioProgress.progress = audio.progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload is AudioEventPayload && payload == AudioEventPayload.IsLoading -> {
|
||||||
|
val audio = post.audio
|
||||||
|
if(audio != null){
|
||||||
|
itemBinding.audio.audioProgress.isIndeterminate = audio.isLoading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload is AudioEventPayload && payload == AudioEventPayload.DurationChanged -> {
|
||||||
|
val audio = post.audio
|
||||||
|
if(audio != null){
|
||||||
|
itemBinding.audio.audioProgress.max = audio.duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload is AudioEventPayload && payload == AudioEventPayload.Ended -> {
|
||||||
|
val audio = post.audio
|
||||||
|
if(audio != null){
|
||||||
|
itemBinding.audio.audioProgress.progress = 0
|
||||||
|
itemBinding.audio.playButton.icon = AppCompatResources.getDrawable(itemView.context, R.drawable.baseline_play_circle_24)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val username: TextView = itemView.findViewById(R.id.text_view_username)
|
val username: TextView = itemView.findViewById(R.id.text_view_username)
|
||||||
username.text = postDto.userName
|
username.text = post.userName
|
||||||
username.setOnClickListener {
|
username.setOnClickListener {
|
||||||
callback.onProfileClick(postDto.userId)
|
callback.onProfileClick(post.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture)
|
val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture)
|
||||||
profileImageView.load(userProfileImage(postDto.userId), imageLoader)
|
profileImageView.load(userProfileImage(post.userId), imageLoader)
|
||||||
|
|
||||||
val dateTextView: TextView = itemView.findViewById(R.id.text_view_date)
|
val dateTextView: TextView = itemView.findViewById(R.id.text_view_date)
|
||||||
dateTextView.text = postDto.date
|
dateTextView.text = post.date
|
||||||
|
|
||||||
val content: TextView = itemView.findViewById(R.id.post_content)
|
val content: TextView = itemView.findViewById(R.id.post_content)
|
||||||
markwon.setMarkdown(content, postDto.textContent)
|
markwon.setMarkdown(content, post.textContent)
|
||||||
|
|
||||||
likeButton.isEnabled = true
|
itemBinding.likeButton.isEnabled = true
|
||||||
|
|
||||||
if(postDto.liked) {
|
if(post.liked) {
|
||||||
likeButton.setIconTintResource(R.color.purple_lighter)
|
itemBinding.likeButton.setIconTintResource(R.color.purple_lighter)
|
||||||
likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
||||||
} else {
|
} else {
|
||||||
likeButton.setIconTintResource(R.color.on_surface)
|
itemBinding.likeButton.setIconTintResource(R.color.on_surface)
|
||||||
likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
||||||
}
|
}
|
||||||
|
|
||||||
likeButton.text = postDto.numberOfLikes.toString()
|
itemBinding.likeButton.text = post.numberOfLikes.toString()
|
||||||
|
|
||||||
commentsButton.text = postDto.numberOfComments.toString()
|
itemBinding.commentButton.text = post.numberOfComments.toString()
|
||||||
|
|
||||||
val moreButton: MaterialButton = itemView.findViewById(R.id.more_button)
|
val moreButton: MaterialButton = itemView.findViewById(R.id.more_button)
|
||||||
moreButton.setOnClickListener {
|
moreButton.setOnClickListener {
|
||||||
callback.onOptions(postDto)
|
callback.onOptions(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
likeButton.setOnClickListener {
|
itemBinding.likeButton.setOnClickListener {
|
||||||
likeButton.isEnabled = false
|
itemBinding.likeButton.isEnabled = false
|
||||||
if(postDto.liked){
|
if(post.liked){
|
||||||
callback.onUnLiked(postDto.id)
|
callback.onUnLiked(post.id)
|
||||||
} else {
|
} else {
|
||||||
callback.onLiked(postDto.id)
|
callback.onLiked(post.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commentsButton.setOnClickListener {
|
itemBinding.commentButton.setOnClickListener {
|
||||||
callback.onComment(postDto.id)
|
callback.onComment(post.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener {
|
itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener {
|
||||||
callback.onOpenPost(postDto.id)
|
callback.onOpenPost(post.id)
|
||||||
|
}
|
||||||
|
if(post.audio != null){
|
||||||
|
itemBinding.audio.apply {
|
||||||
|
root.visibility = View.VISIBLE
|
||||||
|
textViewDescription.text = post.audio.name
|
||||||
|
}
|
||||||
|
itemBinding.audio.playButton.setOnClickListener {
|
||||||
|
callback.onPlay(post.audio)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemBinding.audio.root.visibility = View.GONE
|
||||||
|
itemBinding.audio.playButton.setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,11 +158,138 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
|||||||
data class LikeCountUpdatePayload(val likeCount: Int)
|
data class LikeCountUpdatePayload(val likeCount: Int)
|
||||||
data class CommentsCountUpdatePayload(val commentsCount: Int)
|
data class CommentsCountUpdatePayload(val commentsCount: Int)
|
||||||
|
|
||||||
|
private var currentAudio: Audio? = null
|
||||||
|
private var currentAudioPosition: Int = -1
|
||||||
|
enum class AudioEventPayload {
|
||||||
|
ProgressChanged, IsLoading, IsPLaying, DurationChanged, Ended
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIsPlaying(isPlaying: Boolean, audio: Audio) {
|
||||||
|
if(audio == currentAudio) {
|
||||||
|
currentAudio?.isPlaying = isPlaying
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentAudio?.isPlaying = false
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||||
|
} else {
|
||||||
|
if(postList != null) {
|
||||||
|
for((index, post) in postList!!.withIndex()){
|
||||||
|
post.audio?.isPlaying = false
|
||||||
|
post.audio?.progress = 0
|
||||||
|
post.audio?.isLoading = false
|
||||||
|
if(post.audio != null) {
|
||||||
|
notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||||
|
Log.d(LOG_TAG, "setIsPlaying currentAudioPosition: $currentAudioPosition")
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
currentAudio = postList?.get(currentAudioPosition)?.audio?.also { it.isPlaying = isPlaying }
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIsLoading(isLoading: Boolean, audio: Audio) {
|
||||||
|
if(audio == currentAudio) {
|
||||||
|
currentAudio?.isLoading = isLoading
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentAudio?.isPlaying = false
|
||||||
|
currentAudio?.isLoading = false
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||||
|
|
||||||
|
Log.d(LOG_TAG, "setIsLoading currentAudioPosition: $currentAudioPosition")
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
postList?.get(currentAudioPosition)?.audio?.isLoading = isLoading
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProgress(progress: Int, audio: Audio){
|
||||||
|
if(audio == currentAudio) {
|
||||||
|
audio.progress = progress
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentAudio?.isPlaying = false
|
||||||
|
currentAudio?.progress = 0
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||||
|
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
postList?.get(currentAudioPosition)?.audio?.progress = progress
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDuration(duration: Int, audio: Audio) {
|
||||||
|
if(audio == currentAudio) {
|
||||||
|
audio.duration = duration
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentAudio?.isPlaying = false
|
||||||
|
|
||||||
|
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
postList?.get(currentAudioPosition)?.audio?.duration = duration
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEnded(audio: Audio) {
|
||||||
|
if(audio == currentAudio) {
|
||||||
|
audio.isPlaying = false
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentAudio?.isPlaying = false
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||||
|
|
||||||
|
if(currentAudioPosition > -1) {
|
||||||
|
postList?.get(currentAudioPosition)?.audio?.isPlaying = false
|
||||||
|
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.post_layout, parent, false)
|
return FeedViewHolder(PostLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
||||||
return FeedViewHolder(view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousSize = 0
|
var previousSize = 0
|
||||||
@ -197,4 +366,8 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LOG_TAG = "PostsRecyclerViewAdapter"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -106,27 +106,57 @@ class ProfileMainFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val audioPlayerConnectorListener = object: AudioPlayerConnector.Listener {
|
private val audioPlayerConnectorListener = object: AudioPlayerConnector.Listener {
|
||||||
override fun onPlaying(isPlaying: Boolean, audio: Playable) {
|
override fun onPlaying(isPlaying: Boolean, audio: Playable) {
|
||||||
viewBinding.playButton.icon = AppCompatResources.getDrawable(requireContext(), if(isPlaying) R.drawable.baseline_pause_circle_24 else R.drawable.baseline_play_circle_24)
|
Log.d(LOG_TAG, "onPlaying() isPlaying: $isPlaying: audio $audio")
|
||||||
|
if(audio == audioDescriptionAudio) {
|
||||||
|
viewBinding.playButton.icon =
|
||||||
|
AppCompatResources.getDrawable(
|
||||||
|
requireContext(),
|
||||||
|
if(isPlaying) R.drawable.baseline_pause_circle_24 else R.drawable.baseline_play_circle_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(audio is Audio)
|
||||||
|
postsAdapter.setIsPlaying(isPlaying, audio)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isLoading(isLoading: Boolean, audio: Playable) {
|
override fun isLoading(isLoading: Boolean, audio: Playable) {
|
||||||
viewBinding.playButton.isEnabled = !isLoading
|
if(audio == audioDescriptionAudio) {
|
||||||
viewBinding.audioProgress.isIndeterminate = isLoading
|
viewBinding.playButton.isEnabled = !isLoading
|
||||||
|
viewBinding.audioProgress.isIndeterminate = isLoading
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audio is Audio)
|
||||||
|
postsAdapter.setIsLoading(isLoading, audio)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun progressChanged(second: Int, audio: Playable) {
|
override fun progressChanged(second: Int, audio: Playable) {
|
||||||
viewBinding.audioProgress.setProgress(second, true)
|
if(audio == audioDescriptionAudio) {
|
||||||
|
viewBinding.audioProgress.setProgress(second, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audio is Audio)
|
||||||
|
postsAdapter.setProgress(second, audio)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun durationChanged(duration: Int, audio: Playable) {
|
override fun durationChanged(duration: Int, audio: Playable) {
|
||||||
viewBinding.audioProgress.max = duration
|
if(audio == audioDescriptionAudio) {
|
||||||
|
viewBinding.audioProgress.max = duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audio is Audio)
|
||||||
|
postsAdapter.setDuration(duration, audio)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEnded(audio: Playable) {
|
override fun onEnded(audio: Playable) {
|
||||||
viewBinding.audioProgress.progress = 0
|
if(audio == audioDescriptionAudio) {
|
||||||
|
viewBinding.audioProgress.progress = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audio is Audio)
|
||||||
|
postsAdapter.setEnded(audio)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -431,6 +461,10 @@ class ProfileMainFragment : Fragment() {
|
|||||||
//ProfileActivity.startActivity(requireContext(), userId)
|
//ProfileActivity.startActivity(requireContext(), userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPlay(audio: Audio) {
|
||||||
|
audioPlayerConnector.playPauseAudio(audio)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLoadMore() {
|
override fun onLoadMore() {
|
||||||
viewModel.getFeed(false)
|
viewModel.getFeed(false)
|
||||||
}
|
}
|
||||||
@ -474,5 +508,6 @@ class ProfileMainFragment : Fragment() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CALLER_ID = 30
|
const val CALLER_ID = 30
|
||||||
|
const val LOG_TAG = "ProfileMainFragment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,8 +4,13 @@ import android.Manifest
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
import dagger.hilt.EntryPoint
|
import dagger.hilt.EntryPoint
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import retrofit2.awaitResponse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -19,7 +24,20 @@ class FcmService : FirebaseMessagingService() {
|
|||||||
|
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
|
|
||||||
pushNotificationsApi.registerDevice(token)
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
Log.d(LOG_TAG, token)
|
val response = pushNotificationsApi.registerDevice(token).awaitResponse()
|
||||||
|
Log.d(LOG_TAG, "Device registered. FCM token: $token")
|
||||||
|
Log.d(LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessageReceived(message: RemoteMessage) {
|
||||||
|
super.onMessageReceived(message)
|
||||||
|
|
||||||
|
Log.d(LOG_TAG, "Message received")
|
||||||
|
message.data.forEach { t, u ->
|
||||||
|
Log.d(LOG_TAG, "$t $u")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9,6 +9,6 @@ interface PushNotificationsApi {
|
|||||||
|
|
||||||
@PUT("/api/push_notifications/register_device")
|
@PUT("/api/push_notifications/register_device")
|
||||||
@Multipart
|
@Multipart
|
||||||
fun registerDevice(@Part("token") token: String): Call<Any>
|
fun registerDevice(@Part("token") token: String): Call<Unit>
|
||||||
|
|
||||||
}
|
}
|
||||||
65
app/src/main/res/layout/notification_item.xml
Normal file
65
app/src/main/res/layout/notification_item.xml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
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="80dp"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:layout_marginVertical="4dp">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/notification_main_image"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/notification_secondary_image"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/notification_main_image"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/notification_main_image"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||||
|
tools:src="@drawable/baseline_image_24" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notification_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/notification_message"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/notification_main_image"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:maxLines="1"
|
||||||
|
tools:text="Notification title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notification_message"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/notification_main_image"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notification_title"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
tools:text="Notification message" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|
||||||
@ -56,6 +56,11 @@
|
|||||||
android:gravity="end"/>
|
android:gravity="end"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
<include android:id="@+id/audio"
|
||||||
|
layout="@layout/audio_attachment"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/post_content"
|
android:id="@+id/post_content"
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/notificationsFragment"
|
android:id="@+id/notificationsFragment"
|
||||||
android:name="com.isolaatti.home.notifications.ui.NotificationsFragment"
|
android:name="com.isolaatti.notifications.ui.NotificationsFragment"
|
||||||
android:label="fragment_notifications"
|
android:label="fragment_notifications"
|
||||||
tools:layout="@layout/fragment_notifications" >
|
tools:layout="@layout/fragment_notifications" >
|
||||||
<action
|
<action
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user