Loading app/src/main/java/org/lineageos/twelve/fragments/AddOrRemoveFromPlaylistsFragment.kt +15 −18 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -28,10 +28,10 @@ import kotlinx.coroutines.launch import org.lineageos.twelve.R import org.lineageos.twelve.ext.getParcelable import org.lineageos.twelve.ext.getViewProperty import org.lineageos.twelve.ext.navigateSafe import org.lineageos.twelve.ext.setProgressCompat import org.lineageos.twelve.models.Playlist import org.lineageos.twelve.models.RequestStatus import org.lineageos.twelve.ui.dialogs.EditTextMaterialAlertDialogBuilder import org.lineageos.twelve.ui.recyclerview.SimpleListAdapter import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.ui.views.ListItem Loading Loading @@ -65,7 +65,13 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov view.setOnClickListener { item?.let { when (it === addNewPlaylistItem) { true -> openCreateNewPlaylistDialog() true -> findNavController().navigateSafe( R.id.action_addOrRemoveFromPlaylistsFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.providerOfAudio.value, ) ) false -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { when (it.second) { Loading Loading @@ -120,7 +126,12 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov recyclerView.adapter = adapter createNewPlaylistButton.setOnClickListener { openCreateNewPlaylistDialog() findNavController().navigateSafe( R.id.action_addOrRemoveFromPlaylistsFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.providerOfAudio.value, ) ) } viewModel.loadAudio(audioUri) Loading Loading @@ -178,20 +189,6 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov } } private fun openCreateNewPlaylistDialog() { EditTextMaterialAlertDialogBuilder(requireContext()) .setPositiveButton(R.string.create_playlist_confirm) { text -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist(text) } } } .setTitle(R.string.create_playlist) .setNegativeButton(android.R.string.cancel, null) .show() } companion object { private val LOG_TAG = AddOrRemoveFromPlaylistsFragment::class.simpleName!! Loading app/src/main/java/org/lineageos/twelve/fragments/CreatePlaylistDialogFragment.kt 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ package org.lineageos.twelve.fragments import android.os.Bundle import android.view.View import androidx.core.os.bundleOf import androidx.core.widget.doOnTextChanged import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import com.google.android.material.button.MaterialButton import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.google.android.material.textfield.TextInputLayout import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.lineageos.twelve.R import org.lineageos.twelve.ext.getParcelable import org.lineageos.twelve.ext.getViewProperty import org.lineageos.twelve.ext.selectItem import org.lineageos.twelve.models.ProviderIdentifier import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.viewmodels.CreatePlaylistViewModel class CreatePlaylistDialogFragment : MaterialDialogFragment( R.layout.fragment_create_playlist_dialog ) { // View models private val viewModel by viewModels<CreatePlaylistViewModel>() // Views private val cancelMaterialButton by getViewProperty<MaterialButton>(R.id.cancelMaterialButton) private val createMaterialButton by getViewProperty<MaterialButton>(R.id.createMaterialButton) private val fullscreenLoadingProgressBar by getViewProperty<FullscreenLoadingProgressBar>(R.id.fullscreenLoadingProgressBar) private val playlistNameTextInputLayout by getViewProperty<TextInputLayout>(R.id.playlistNameTextInputLayout) private val providerAutoCompleteTextView by getViewProperty<MaterialAutoCompleteTextView>(R.id.providerAutoCompleteTextView) private val providerTextInputLayout by getViewProperty<TextInputLayout>(R.id.providerTextInputLayout) // Arguments private val providerIdentifier: ProviderIdentifier? get() = arguments?.getParcelable(ARG_PROVIDER_IDENTIFIER, ProviderIdentifier::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.setProviderIdentifier(providerIdentifier) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) providerAutoCompleteTextView.setOnItemClickListener { _, _, position, _ -> viewModel.setProviderPosition(position) } playlistNameTextInputLayout.editText!!.apply { setText(viewModel.getPlaylistName()) doOnTextChanged { text, _, _, _ -> playlistNameTextInputLayout.error = null viewModel.setPlaylistName(text?.toString() ?: "") } } cancelMaterialButton.setOnClickListener { findNavController().navigateUp() } createMaterialButton.setOnClickListener { if (viewModel.isPlaylistNameEmpty()) { playlistNameTextInputLayout.error = getString( R.string.create_playlist_error_empty_name ) return@setOnClickListener } playlistNameTextInputLayout.error = null viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist() findNavController().navigateUp() } } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { loadData() } } } private fun CoroutineScope.loadData() { launch { viewModel.providersWithSelection.collectLatest { providersWithSelection -> val (providers, position) = providersWithSelection providerAutoCompleteTextView.setSimpleItems( providers.map { provider -> getString( R.string.provider_format, provider.name, getString(provider.type.nameStringResId), ) }.toTypedArray() ) position?.also { val provider = providers[it] providerAutoCompleteTextView.selectItem(it) providerTextInputLayout.setStartIconDrawable( provider.type.iconDrawableResId ) } } } } companion object { private const val ARG_PROVIDER_IDENTIFIER = "provider_identifier" /** * Create a [Bundle] to use as the arguments for this fragment. * @param providerIdentifier A [ProviderIdentifier] to pre-fill the provider field */ fun createBundle( providerIdentifier: ProviderIdentifier? = null, ) = bundleOf( ARG_PROVIDER_IDENTIFIER to providerIdentifier, ) } } app/src/main/java/org/lineageos/twelve/fragments/PlaylistsFragment.kt +14 −20 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -30,10 +30,8 @@ import org.lineageos.twelve.ext.setProgressCompat import org.lineageos.twelve.models.Playlist import org.lineageos.twelve.models.RequestStatus import org.lineageos.twelve.models.SortingStrategy import org.lineageos.twelve.ui.dialogs.EditTextMaterialAlertDialogBuilder import org.lineageos.twelve.ui.recyclerview.SimpleListAdapter import org.lineageos.twelve.ui.recyclerview.UniqueItemDiffCallback import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.ui.views.ListItem import org.lineageos.twelve.ui.views.SortingChip import org.lineageos.twelve.utils.PermissionsChecker Loading @@ -49,7 +47,6 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { // Views private val createNewPlaylistButton by getViewProperty<Button>(R.id.createNewPlaylistButton) private val fullscreenLoadingProgressBar by getViewProperty<FullscreenLoadingProgressBar>(R.id.fullscreenLoadingProgressBar) private val linearProgressIndicator by getViewProperty<LinearProgressIndicator>(R.id.linearProgressIndicator) private val noElementsLinearLayout by getViewProperty<LinearLayout>(R.id.noElementsLinearLayout) private val recyclerView by getViewProperty<RecyclerView>(R.id.recyclerView) Loading @@ -65,7 +62,13 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { view.setOnClickListener { item?.let { when (it === addNewPlaylistItem) { true -> openCreateNewPlaylistDialog() true -> findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.navigationProvider.value ) ) false -> findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_playlist, PlaylistFragment.createBundle(it.uri) Loading Loading @@ -124,7 +127,12 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { recyclerView.adapter = adapter createNewPlaylistButton.setOnClickListener { openCreateNewPlaylistDialog() findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.navigationProvider.value ) ) } viewLifecycleOwner.lifecycleScope.launch { Loading Loading @@ -194,20 +202,6 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { } } private fun openCreateNewPlaylistDialog() { EditTextMaterialAlertDialogBuilder(requireContext()) .setPositiveButton(R.string.create_playlist_confirm) { text -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist(text) } } } .setTitle(R.string.create_playlist) .setNegativeButton(android.R.string.cancel, null) .show() } companion object { private val LOG_TAG = PlaylistsFragment::class.simpleName!! } Loading app/src/main/java/org/lineageos/twelve/viewmodels/AddOrRemoveFromPlaylistsViewModel.kt +12 −1 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -48,6 +48,17 @@ class AddOrRemoveFromPlaylistsViewModel(application: Application) : TwelveViewMo RequestStatus.Loading() ) @OptIn(ExperimentalCoroutinesApi::class) val providerOfAudio = audioUri .filterNotNull() .flatMapLatest { mediaRepository.providerOfMediaItems(it) } .flowOn(Dispatchers.IO) .stateIn( viewModelScope, SharingStarted.Eagerly, null ) fun loadAudio(audioUri: Uri) { this.audioUri.value = audioUri } Loading app/src/main/java/org/lineageos/twelve/viewmodels/CreatePlaylistViewModel.kt 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ package org.lineageos.twelve.viewmodels import android.app.Application import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import org.lineageos.twelve.datasources.MediaError import org.lineageos.twelve.models.Provider import org.lineageos.twelve.models.ProviderIdentifier import org.lineageos.twelve.models.RequestStatus class CreatePlaylistViewModel(application: Application) : TwelveViewModel(application) { private val providerIdentifier = MutableStateFlow<ProviderIdentifier?>(null) private val playlistName = MutableStateFlow("") val providersWithSelection = combine( mediaRepository.allVisibleProviders, providerIdentifier ) { allVisibleProviders, providerIdentifier -> allVisibleProviders to providerIdentifier?.let { allVisibleProviders.indexOfFirst { provider -> provider.type == it.type && provider.typeId == it.typeId }.takeIf { it != -1 } } } .flowOn(Dispatchers.IO) .stateIn( viewModelScope, SharingStarted.WhileSubscribed(), listOf<Provider>() to null ) fun setProviderIdentifier(providerIdentifier: ProviderIdentifier?) { this.providerIdentifier.value = providerIdentifier } fun setProviderPosition(position: Int) = setProviderIdentifier( providersWithSelection.value.first[position] ) fun getPlaylistName() = playlistName.value fun setPlaylistName(playlistName: String) { this.playlistName.value = playlistName } fun isPlaylistNameEmpty() = playlistName.value.isEmpty() suspend fun createPlaylist() = providerIdentifier.value?.let { withContext(Dispatchers.IO) { mediaRepository.createPlaylist(it, playlistName.value) } } ?: RequestStatus.Error(MediaError.IO) } Loading
app/src/main/java/org/lineageos/twelve/fragments/AddOrRemoveFromPlaylistsFragment.kt +15 −18 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -28,10 +28,10 @@ import kotlinx.coroutines.launch import org.lineageos.twelve.R import org.lineageos.twelve.ext.getParcelable import org.lineageos.twelve.ext.getViewProperty import org.lineageos.twelve.ext.navigateSafe import org.lineageos.twelve.ext.setProgressCompat import org.lineageos.twelve.models.Playlist import org.lineageos.twelve.models.RequestStatus import org.lineageos.twelve.ui.dialogs.EditTextMaterialAlertDialogBuilder import org.lineageos.twelve.ui.recyclerview.SimpleListAdapter import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.ui.views.ListItem Loading Loading @@ -65,7 +65,13 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov view.setOnClickListener { item?.let { when (it === addNewPlaylistItem) { true -> openCreateNewPlaylistDialog() true -> findNavController().navigateSafe( R.id.action_addOrRemoveFromPlaylistsFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.providerOfAudio.value, ) ) false -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { when (it.second) { Loading Loading @@ -120,7 +126,12 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov recyclerView.adapter = adapter createNewPlaylistButton.setOnClickListener { openCreateNewPlaylistDialog() findNavController().navigateSafe( R.id.action_addOrRemoveFromPlaylistsFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.providerOfAudio.value, ) ) } viewModel.loadAudio(audioUri) Loading Loading @@ -178,20 +189,6 @@ class AddOrRemoveFromPlaylistsFragment : Fragment(R.layout.fragment_add_or_remov } } private fun openCreateNewPlaylistDialog() { EditTextMaterialAlertDialogBuilder(requireContext()) .setPositiveButton(R.string.create_playlist_confirm) { text -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist(text) } } } .setTitle(R.string.create_playlist) .setNegativeButton(android.R.string.cancel, null) .show() } companion object { private val LOG_TAG = AddOrRemoveFromPlaylistsFragment::class.simpleName!! Loading
app/src/main/java/org/lineageos/twelve/fragments/CreatePlaylistDialogFragment.kt 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ package org.lineageos.twelve.fragments import android.os.Bundle import android.view.View import androidx.core.os.bundleOf import androidx.core.widget.doOnTextChanged import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import com.google.android.material.button.MaterialButton import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.google.android.material.textfield.TextInputLayout import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.lineageos.twelve.R import org.lineageos.twelve.ext.getParcelable import org.lineageos.twelve.ext.getViewProperty import org.lineageos.twelve.ext.selectItem import org.lineageos.twelve.models.ProviderIdentifier import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.viewmodels.CreatePlaylistViewModel class CreatePlaylistDialogFragment : MaterialDialogFragment( R.layout.fragment_create_playlist_dialog ) { // View models private val viewModel by viewModels<CreatePlaylistViewModel>() // Views private val cancelMaterialButton by getViewProperty<MaterialButton>(R.id.cancelMaterialButton) private val createMaterialButton by getViewProperty<MaterialButton>(R.id.createMaterialButton) private val fullscreenLoadingProgressBar by getViewProperty<FullscreenLoadingProgressBar>(R.id.fullscreenLoadingProgressBar) private val playlistNameTextInputLayout by getViewProperty<TextInputLayout>(R.id.playlistNameTextInputLayout) private val providerAutoCompleteTextView by getViewProperty<MaterialAutoCompleteTextView>(R.id.providerAutoCompleteTextView) private val providerTextInputLayout by getViewProperty<TextInputLayout>(R.id.providerTextInputLayout) // Arguments private val providerIdentifier: ProviderIdentifier? get() = arguments?.getParcelable(ARG_PROVIDER_IDENTIFIER, ProviderIdentifier::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.setProviderIdentifier(providerIdentifier) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) providerAutoCompleteTextView.setOnItemClickListener { _, _, position, _ -> viewModel.setProviderPosition(position) } playlistNameTextInputLayout.editText!!.apply { setText(viewModel.getPlaylistName()) doOnTextChanged { text, _, _, _ -> playlistNameTextInputLayout.error = null viewModel.setPlaylistName(text?.toString() ?: "") } } cancelMaterialButton.setOnClickListener { findNavController().navigateUp() } createMaterialButton.setOnClickListener { if (viewModel.isPlaylistNameEmpty()) { playlistNameTextInputLayout.error = getString( R.string.create_playlist_error_empty_name ) return@setOnClickListener } playlistNameTextInputLayout.error = null viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist() findNavController().navigateUp() } } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { loadData() } } } private fun CoroutineScope.loadData() { launch { viewModel.providersWithSelection.collectLatest { providersWithSelection -> val (providers, position) = providersWithSelection providerAutoCompleteTextView.setSimpleItems( providers.map { provider -> getString( R.string.provider_format, provider.name, getString(provider.type.nameStringResId), ) }.toTypedArray() ) position?.also { val provider = providers[it] providerAutoCompleteTextView.selectItem(it) providerTextInputLayout.setStartIconDrawable( provider.type.iconDrawableResId ) } } } } companion object { private const val ARG_PROVIDER_IDENTIFIER = "provider_identifier" /** * Create a [Bundle] to use as the arguments for this fragment. * @param providerIdentifier A [ProviderIdentifier] to pre-fill the provider field */ fun createBundle( providerIdentifier: ProviderIdentifier? = null, ) = bundleOf( ARG_PROVIDER_IDENTIFIER to providerIdentifier, ) } }
app/src/main/java/org/lineageos/twelve/fragments/PlaylistsFragment.kt +14 −20 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -30,10 +30,8 @@ import org.lineageos.twelve.ext.setProgressCompat import org.lineageos.twelve.models.Playlist import org.lineageos.twelve.models.RequestStatus import org.lineageos.twelve.models.SortingStrategy import org.lineageos.twelve.ui.dialogs.EditTextMaterialAlertDialogBuilder import org.lineageos.twelve.ui.recyclerview.SimpleListAdapter import org.lineageos.twelve.ui.recyclerview.UniqueItemDiffCallback import org.lineageos.twelve.ui.views.FullscreenLoadingProgressBar import org.lineageos.twelve.ui.views.ListItem import org.lineageos.twelve.ui.views.SortingChip import org.lineageos.twelve.utils.PermissionsChecker Loading @@ -49,7 +47,6 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { // Views private val createNewPlaylistButton by getViewProperty<Button>(R.id.createNewPlaylistButton) private val fullscreenLoadingProgressBar by getViewProperty<FullscreenLoadingProgressBar>(R.id.fullscreenLoadingProgressBar) private val linearProgressIndicator by getViewProperty<LinearProgressIndicator>(R.id.linearProgressIndicator) private val noElementsLinearLayout by getViewProperty<LinearLayout>(R.id.noElementsLinearLayout) private val recyclerView by getViewProperty<RecyclerView>(R.id.recyclerView) Loading @@ -65,7 +62,13 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { view.setOnClickListener { item?.let { when (it === addNewPlaylistItem) { true -> openCreateNewPlaylistDialog() true -> findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.navigationProvider.value ) ) false -> findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_playlist, PlaylistFragment.createBundle(it.uri) Loading Loading @@ -124,7 +127,12 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { recyclerView.adapter = adapter createNewPlaylistButton.setOnClickListener { openCreateNewPlaylistDialog() findNavController().navigateSafe( R.id.action_mainFragment_to_fragment_create_playlist_dialog, CreatePlaylistDialogFragment.createBundle( providerIdentifier = viewModel.navigationProvider.value ) ) } viewLifecycleOwner.lifecycleScope.launch { Loading Loading @@ -194,20 +202,6 @@ class PlaylistsFragment : Fragment(R.layout.fragment_playlists) { } } private fun openCreateNewPlaylistDialog() { EditTextMaterialAlertDialogBuilder(requireContext()) .setPositiveButton(R.string.create_playlist_confirm) { text -> viewLifecycleOwner.lifecycleScope.launch { fullscreenLoadingProgressBar.withProgress { viewModel.createPlaylist(text) } } } .setTitle(R.string.create_playlist) .setNegativeButton(android.R.string.cancel, null) .show() } companion object { private val LOG_TAG = PlaylistsFragment::class.simpleName!! } Loading
app/src/main/java/org/lineageos/twelve/viewmodels/AddOrRemoveFromPlaylistsViewModel.kt +12 −1 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2024 The LineageOS Project * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ Loading Loading @@ -48,6 +48,17 @@ class AddOrRemoveFromPlaylistsViewModel(application: Application) : TwelveViewMo RequestStatus.Loading() ) @OptIn(ExperimentalCoroutinesApi::class) val providerOfAudio = audioUri .filterNotNull() .flatMapLatest { mediaRepository.providerOfMediaItems(it) } .flowOn(Dispatchers.IO) .stateIn( viewModelScope, SharingStarted.Eagerly, null ) fun loadAudio(audioUri: Uri) { this.audioUri.value = audioUri } Loading
app/src/main/java/org/lineageos/twelve/viewmodels/CreatePlaylistViewModel.kt 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2025 The LineageOS Project * SPDX-License-Identifier: Apache-2.0 */ package org.lineageos.twelve.viewmodels import android.app.Application import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import org.lineageos.twelve.datasources.MediaError import org.lineageos.twelve.models.Provider import org.lineageos.twelve.models.ProviderIdentifier import org.lineageos.twelve.models.RequestStatus class CreatePlaylistViewModel(application: Application) : TwelveViewModel(application) { private val providerIdentifier = MutableStateFlow<ProviderIdentifier?>(null) private val playlistName = MutableStateFlow("") val providersWithSelection = combine( mediaRepository.allVisibleProviders, providerIdentifier ) { allVisibleProviders, providerIdentifier -> allVisibleProviders to providerIdentifier?.let { allVisibleProviders.indexOfFirst { provider -> provider.type == it.type && provider.typeId == it.typeId }.takeIf { it != -1 } } } .flowOn(Dispatchers.IO) .stateIn( viewModelScope, SharingStarted.WhileSubscribed(), listOf<Provider>() to null ) fun setProviderIdentifier(providerIdentifier: ProviderIdentifier?) { this.providerIdentifier.value = providerIdentifier } fun setProviderPosition(position: Int) = setProviderIdentifier( providersWithSelection.value.first[position] ) fun getPlaylistName() = playlistName.value fun setPlaylistName(playlistName: String) { this.playlistName.value = playlistName } fun isPlaylistNameEmpty() = playlistName.value.isEmpty() suspend fun createPlaylist() = providerIdentifier.value?.let { withContext(Dispatchers.IO) { mediaRepository.createPlaylist(it, playlistName.value) } } ?: RequestStatus.Error(MediaError.IO) }