WIP audios
This commit is contained in:
parent
a1c8b49607
commit
0322f79579
24
app/src/main/java/com/isolaatti/audio/Module.kt
Normal file
24
app/src/main/java/com/isolaatti/audio/Module.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.isolaatti.audio
|
||||||
|
|
||||||
|
import com.isolaatti.audio.common.data.AudiosApi
|
||||||
|
import com.isolaatti.audio.common.data.AudiosRepositoryImpl
|
||||||
|
import com.isolaatti.audio.common.domain.AudiosRepository
|
||||||
|
import com.isolaatti.connectivity.RetrofitClient
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class Module {
|
||||||
|
@Provides
|
||||||
|
fun provideAudiosApi(retrofitClient: RetrofitClient): AudiosApi {
|
||||||
|
return retrofitClient.client.create(AudiosApi::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideAudiosRepository(audiosApi: AudiosApi): AudiosRepository {
|
||||||
|
return AudiosRepositoryImpl(audiosApi)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.isolaatti.audio.audios_list.presentation
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
|
import coil.load
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
import com.isolaatti.databinding.AudioListItemBinding
|
||||||
|
|
||||||
|
class AudiosAdapter(
|
||||||
|
private val onClick: ((audio: Audio) -> Unit),
|
||||||
|
private val onOptionsClick: ((audio: Audio, button: View) -> Boolean)
|
||||||
|
) : RecyclerView.Adapter<AudiosAdapter.AudiosViewHolder>() {
|
||||||
|
|
||||||
|
private var data: List<Audio> = listOf()
|
||||||
|
|
||||||
|
fun setData(audios: List<Audio>) {
|
||||||
|
data = audios
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class AudiosViewHolder(val binding: AudioListItemBinding) : ViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AudiosViewHolder {
|
||||||
|
val binding = AudioListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return AudiosViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return data.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: AudiosViewHolder, position: Int) {
|
||||||
|
val audio = data[position]
|
||||||
|
|
||||||
|
holder.binding.audioName.text = audio.name
|
||||||
|
holder.binding.audioAuthor.text = audio.userName
|
||||||
|
holder.binding.thumbnail.load(audio.thumbnail)
|
||||||
|
|
||||||
|
holder.binding.root.setOnClickListener {
|
||||||
|
onClick(audio)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.binding.audioItemOptionsButton.setOnClickListener {
|
||||||
|
onOptionsClick(audio, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.isolaatti.audio.audios_list.presentation
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
import com.isolaatti.audio.common.domain.AudiosRepository
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class AudiosViewModel @Inject constructor(private val audiosRepository: AudiosRepository) : ViewModel() {
|
||||||
|
val resource: MutableLiveData<Resource<List<Audio>>> = MutableLiveData()
|
||||||
|
|
||||||
|
|
||||||
|
fun loadAudios(userId: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
audiosRepository.getAudiosOfUser(userId, null).onEach {
|
||||||
|
resource.postValue(it)
|
||||||
|
|
||||||
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
package com.isolaatti.audio.audios_list.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.widget.PopupMenu
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.audio.audios_list.presentation.AudiosAdapter
|
||||||
|
import com.isolaatti.audio.audios_list.presentation.AudiosViewModel
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
import com.isolaatti.common.ErrorMessageViewModel
|
||||||
|
import com.isolaatti.databinding.FragmentAudiosBinding
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AudiosFragment : Fragment() {
|
||||||
|
lateinit var viewBinding: FragmentAudiosBinding
|
||||||
|
private val viewModel: AudiosViewModel by viewModels()
|
||||||
|
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||||
|
private val arguments: AudiosFragmentArgs by navArgs()
|
||||||
|
private lateinit var adapter: AudiosAdapter
|
||||||
|
|
||||||
|
private var loadedFirstTime = false
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
viewBinding = FragmentAudiosBinding.inflate(inflater)
|
||||||
|
|
||||||
|
return viewBinding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onOptionsClick: ((audio: Audio, button: View) -> Boolean) = { audio, button ->
|
||||||
|
val popup = PopupMenu(requireContext(), button)
|
||||||
|
popup.menuInflater.inflate(R.menu.audio_item_menu, popup.menu)
|
||||||
|
|
||||||
|
|
||||||
|
popup.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onAudioClick: ((audio: Audio) -> Unit) = {
|
||||||
|
// Play audio
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
adapter = AudiosAdapter(onClick = onAudioClick, onOptionsClick = onOptionsClick)
|
||||||
|
|
||||||
|
viewBinding.recyclerAudios.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||||
|
viewBinding.recyclerAudios.adapter = adapter
|
||||||
|
|
||||||
|
|
||||||
|
setupObservers()
|
||||||
|
if(arguments.source == SOURCE_PROFILE) {
|
||||||
|
viewModel.loadAudios(arguments.sourceId.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupObservers() {
|
||||||
|
viewModel.resource.observe(viewLifecycleOwner) { resource ->
|
||||||
|
when(resource) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
errorViewModel.error.postValue(resource.errorType)
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {
|
||||||
|
if(!loadedFirstTime) {
|
||||||
|
viewBinding.progressBarLoading.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Success -> {
|
||||||
|
viewBinding.progressBarLoading.visibility = View.GONE
|
||||||
|
loadedFirstTime = true
|
||||||
|
adapter.setData(resource.data!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SOURCE_PROFILE = "source_profile"
|
||||||
|
const val SOURCE_SQUAD = "source_squads"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.isolaatti.audio.common.data
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
data class AudiosDto(val data: List<AudioDto>)
|
||||||
|
data class AudioDto(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val creationTime: ZonedDateTime,
|
||||||
|
val userId: Int,
|
||||||
|
val firestoreObjectPath: String,
|
||||||
|
val userName: String
|
||||||
|
)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.isolaatti.audio.common.data
|
||||||
|
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
interface AudiosApi {
|
||||||
|
@GET("/api/Audios/OfUser/{userId}")
|
||||||
|
fun getAudiosOfUser(@Path("userId") userId: Int, @Query("lastAudioId") lastAudioId: String?): Call<AudiosDto>
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.isolaatti.audio.common.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.isolaatti.audio.common.domain.Audio
|
||||||
|
import com.isolaatti.audio.common.domain.AudiosRepository
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import retrofit2.awaitResponse
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AudiosRepositoryImpl @Inject constructor(private val audiosApi: AudiosApi) : AudiosRepository {
|
||||||
|
override fun getAudiosOfUser(userId: Int, lastId: String?): Flow<Resource<List<Audio>>> = flow {
|
||||||
|
emit(Resource.Loading())
|
||||||
|
try {
|
||||||
|
val response = audiosApi.getAudiosOfUser(userId, lastId).awaitResponse()
|
||||||
|
|
||||||
|
if(response.isSuccessful) {
|
||||||
|
val body = response.body()
|
||||||
|
if(body == null) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
||||||
|
return@flow
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(Resource.Success(body.data.map { Audio.fromDto(it) }))
|
||||||
|
}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e("AudiosRepositoryImpl", e.message.toString())
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/src/main/java/com/isolaatti/audio/common/domain/Audio.kt
Normal file
37
app/src/main/java/com/isolaatti/audio/common/domain/Audio.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.isolaatti.audio.common.domain
|
||||||
|
|
||||||
|
import com.isolaatti.audio.common.data.AudioDto
|
||||||
|
import com.isolaatti.common.Ownable
|
||||||
|
import com.isolaatti.connectivity.RetrofitClient.Companion.BASE_URL
|
||||||
|
import com.isolaatti.utils.UrlGen
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
data class Audio(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val creationTime: ZonedDateTime,
|
||||||
|
override val userId: Int,
|
||||||
|
val userName: String
|
||||||
|
): Ownable {
|
||||||
|
var playing: Boolean = false
|
||||||
|
val downloadUrl: String get() {
|
||||||
|
return "${BASE_URL}/$id"
|
||||||
|
}
|
||||||
|
|
||||||
|
val thumbnail: String get() {
|
||||||
|
return UrlGen.userProfileImage(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromDto(audioDto: AudioDto): Audio {
|
||||||
|
return Audio(
|
||||||
|
id = audioDto.id,
|
||||||
|
name = audioDto.name,
|
||||||
|
creationTime = audioDto.creationTime,
|
||||||
|
userId = audioDto.userId,
|
||||||
|
userName = audioDto.userName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.isolaatti.audio.common.domain
|
||||||
|
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface AudiosRepository {
|
||||||
|
fun getAudiosOfUser(userId: Int, lastId: String?): Flow<Resource<List<Audio>>>
|
||||||
|
}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
package com.isolaatti.connectivity
|
package com.isolaatti.connectivity
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
|
import com.isolaatti.type_adapters.LocalDateTimeAdapter
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
import java.io.IOException
|
import java.time.ZonedDateTime
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@ -25,10 +27,14 @@ class RetrofitClient @Inject constructor(private val authenticationInterceptor:
|
|||||||
.addInterceptor(authenticationInterceptor)
|
.addInterceptor(authenticationInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val gson = GsonBuilder()
|
||||||
|
.registerTypeAdapter(ZonedDateTime::class.java, LocalDateTimeAdapter())
|
||||||
|
.create()
|
||||||
|
|
||||||
|
|
||||||
val client: Retrofit get() = Retrofit.Builder()
|
val client: Retrofit get() = Retrofit.Builder()
|
||||||
.baseUrl(BASE_URL)
|
.baseUrl(BASE_URL)
|
||||||
.client(okHttpClient)
|
.client(okHttpClient)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
package com.isolaatti.profile.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.isolaatti.databinding.FragmentAudiosBinding
|
|
||||||
|
|
||||||
class AudiosFragment : Fragment() {
|
|
||||||
lateinit var viewBinding: FragmentAudiosBinding
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
viewBinding = FragmentAudiosBinding.inflate(inflater)
|
|
||||||
|
|
||||||
return viewBinding.root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -19,26 +19,26 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import coil.load
|
import coil.load
|
||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.audio.audios_list.ui.AudiosFragment
|
||||||
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
|
||||||
import com.isolaatti.databinding.FragmentDiscussionsBinding
|
|
||||||
import com.isolaatti.followers.domain.FollowingState
|
|
||||||
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
|
||||||
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
|
||||||
import com.isolaatti.common.Ownable
|
import com.isolaatti.common.Ownable
|
||||||
import com.isolaatti.common.options_bottom_sheet.domain.OptionClicked
|
import com.isolaatti.common.options_bottom_sheet.domain.OptionClicked
|
||||||
import com.isolaatti.common.options_bottom_sheet.domain.Options
|
import com.isolaatti.common.options_bottom_sheet.domain.Options
|
||||||
import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||||
import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
||||||
|
import com.isolaatti.databinding.FragmentDiscussionsBinding
|
||||||
|
import com.isolaatti.followers.domain.FollowingState
|
||||||
import com.isolaatti.images.image_list.ui.ImagesFragment
|
import com.isolaatti.images.image_list.ui.ImagesFragment
|
||||||
import com.isolaatti.images.picture_viewer.ui.PictureViewerActivity
|
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
||||||
import com.isolaatti.posting.posts.domain.entity.Post
|
import com.isolaatti.posting.posts.domain.entity.Post
|
||||||
import com.isolaatti.posting.posts.presentation.CreatePostContract
|
import com.isolaatti.posting.posts.presentation.CreatePostContract
|
||||||
import com.isolaatti.posting.posts.presentation.EditPostContract
|
import com.isolaatti.posting.posts.presentation.EditPostContract
|
||||||
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
|
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
|
||||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||||
|
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||||
import com.isolaatti.profile.domain.entity.UserProfile
|
import com.isolaatti.profile.domain.entity.UserProfile
|
||||||
import com.isolaatti.profile.presentation.ProfileViewModel
|
import com.isolaatti.profile.presentation.ProfileViewModel
|
||||||
import com.isolaatti.utils.UrlGen
|
import com.isolaatti.utils.UrlGen
|
||||||
@ -214,7 +214,7 @@ class ProfileMainFragment : Fragment() {
|
|||||||
viewBinding.bottomAppBar.setOnMenuItemClickListener {
|
viewBinding.bottomAppBar.setOnMenuItemClickListener {
|
||||||
when(it.itemId) {
|
when(it.itemId) {
|
||||||
R.id.audios_menu_item -> {
|
R.id.audios_menu_item -> {
|
||||||
findNavController().navigate(ProfileMainFragmentDirections.actionDiscussionsFragmentToAudiosFragment())
|
findNavController().navigate(ProfileMainFragmentDirections.actionDiscussionsFragmentToAudiosFragment(AudiosFragment.SOURCE_PROFILE, userId.toString()))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.images_menu_item -> {
|
R.id.images_menu_item -> {
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.isolaatti.type_adapters
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext
|
||||||
|
import com.google.gson.JsonDeserializer
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
class LocalDateTimeAdapter : JsonDeserializer<ZonedDateTime> {
|
||||||
|
override fun deserialize(
|
||||||
|
json: JsonElement?,
|
||||||
|
typeOfT: Type?,
|
||||||
|
context: JsonDeserializationContext?
|
||||||
|
): ZonedDateTime {
|
||||||
|
return ZonedDateTime.parse(json?.asString, DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
app/src/main/res/layout/audio_list_item.xml
Normal file
67
app/src/main/res/layout/audio_list_item.xml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
style="?attr/materialCardViewFilledStyle"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/thumbnail"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
tools:src="@drawable/baseline_image_24" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/audio_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:lines="1"
|
||||||
|
android:textSize="16sp"
|
||||||
|
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/audio_author"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/audio_item_options_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/thumbnail"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Hello" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/audio_author"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/audio_item_options_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/thumbnail"
|
||||||
|
tools:text="Erik" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/audio_item_options_button"
|
||||||
|
style="?attr/materialIconButtonFilledTonalStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:icon="@drawable/baseline_more_horiz_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:contentDescription="@string/audio_options"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@ -2,7 +2,8 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@ -15,4 +16,17 @@
|
|||||||
app:navigationIconTint="@color/on_surface"
|
app:navigationIconTint="@color/on_surface"
|
||||||
app:titleCentered="true"/>
|
app:titleCentered="true"/>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_audios"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar_loading"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
9
app/src/main/res/menu/audio_item_menu.xml
Normal file
9
app/src/main/res/menu/audio_item_menu.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/delete_item"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:title="@string/delete" />
|
||||||
|
</menu>
|
||||||
@ -31,8 +31,14 @@
|
|||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/audiosFragment"
|
android:id="@+id/audiosFragment"
|
||||||
android:name="com.isolaatti.profile.ui.AudiosFragment"
|
android:name="com.isolaatti.audio.audios_list.ui.AudiosFragment"
|
||||||
android:label="AudiosFragment" />
|
android:label="AudiosFragment" >
|
||||||
|
<argument
|
||||||
|
android:name="source"
|
||||||
|
app:argType="string" />
|
||||||
|
<argument android:name="sourceId"
|
||||||
|
app:argType="string" />
|
||||||
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/imagesFragment"
|
android:id="@+id/imagesFragment"
|
||||||
android:name="com.isolaatti.images.image_list.ui.ImagesFragment"
|
android:name="com.isolaatti.images.image_list.ui.ImagesFragment"
|
||||||
@ -64,4 +70,7 @@
|
|||||||
android:name="userId"
|
android:name="userId"
|
||||||
app:argType="integer" />
|
app:argType="integer" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
<argument
|
||||||
|
android:name="userId"
|
||||||
|
app:argType="integer" />
|
||||||
</navigation>
|
</navigation>
|
||||||
@ -121,4 +121,5 @@
|
|||||||
<string name="delete_images_dialog_message">Remove %d images?</string>
|
<string name="delete_images_dialog_message">Remove %d images?</string>
|
||||||
<string name="selected_images_count">Images selected: %d</string>
|
<string name="selected_images_count">Images selected: %d</string>
|
||||||
<string name="picture_name">Picture name</string>
|
<string name="picture_name">Picture name</string>
|
||||||
|
<string name="audio_options">Audio options</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user