Skip to content

Commit

Permalink
Merge pull request #570 from willowtreeapps/bugfix/557-cannot-edit-us…
Browse files Browse the repository at this point in the history
…er-added-phrase-2

Use Flow of phrases from Room rather than a one shot
  • Loading branch information
PaulKlauser authored Jun 12, 2024
2 parents cc52fc1 + 21043d0 commit c6da557
Show file tree
Hide file tree
Showing 22 changed files with 123 additions and 158 deletions.
3 changes: 1 addition & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ dependencies {
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:viewmodel:1.1.1'

implementation 'io.insert-koin:koin-core:3.1.5'
implementation 'io.insert-koin:koin-android:3.1.5'
implementation(libs.koin.android)

implementation "androidx.room:room-runtime:2.5.2"
kapt "androidx.room:room-compiler:2.5.2"
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/com/willowtree/vocable/AppKoinModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ val vocableKoinModule = module {
single<VocableEnvironment> { VocableEnvironmentImpl() }
viewModel { PresetsViewModel(get(), get(), get(named<PresetsViewModel>())) }
viewModel { EditCategoriesViewModel(get()) }
viewModel { EditCategoryPhrasesViewModel(get(), get()) }
viewModel { EditCategoryPhrasesViewModel(get(), get(), get()) }
viewModel { AddUpdateCategoryViewModel(get(), get(), get()) }
viewModel { EditCategoryMenuViewModel(get()) }
}
3 changes: 3 additions & 0 deletions app/src/main/java/com/willowtree/vocable/IPhrasesUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package com.willowtree.vocable

import com.willowtree.vocable.presets.Phrase
import com.willowtree.vocable.utils.locale.LocalesWithText
import kotlinx.coroutines.flow.Flow

interface IPhrasesUseCase {

suspend fun getPhrasesForCategory(categoryId: String): List<Phrase>

fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<Phrase>>

suspend fun updatePhraseLastSpokenTime(phraseId: String)

suspend fun deletePhrase(phraseId: String)
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/com/willowtree/vocable/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import io.github.inflationx.viewpump.ViewPumpContextWrapper
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.scope.ScopeActivity
import org.koin.androidx.viewmodel.ViewModelOwner
import org.koin.androidx.viewmodel.scope.getViewModel
import org.koin.androidx.viewmodel.ext.android.getViewModel

class MainActivity : ScopeActivity() {

Expand All @@ -41,7 +40,7 @@ class MainActivity : ScopeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

faceTrackingViewModel = scope.getViewModel(owner = { ViewModelOwner.from(this) })
faceTrackingViewModel = getViewModel()

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Expand Down
47 changes: 36 additions & 11 deletions app/src/main/java/com/willowtree/vocable/PhrasesUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.willowtree.vocable.utils.DateProvider
import com.willowtree.vocable.utils.UUIDProvider
import com.willowtree.vocable.utils.locale.LocaleProvider
import com.willowtree.vocable.utils.locale.LocalesWithText
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine

class PhrasesUseCase(
private val legacyPhrasesRepository: ILegacyCategoriesAndPhrasesRepository,
Expand All @@ -31,6 +33,23 @@ class PhrasesUseCase(
presetPhrasesRepository.getPhrasesForCategory(categoryId)
}

override fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<Phrase>> {
return combine(
presetPhrasesRepository.getRecentPhrasesFlow(),
storedPhrasesRepository.getRecentPhrasesFlow(),
storedPhrasesRepository.getPhrasesForCategoryFlow(categoryId),
presetPhrasesRepository.getPhrasesForCategoryFlow(categoryId)
) { recentPresets, recentStored, stored, presets ->
if (categoryId == PresetCategories.RECENTS.id) {
(recentPresets + recentStored)
.sortedByDescending { it.lastSpokenDate }
.take(8)
} else {
stored + presets
}
}
}

override suspend fun updatePhraseLastSpokenTime(phraseId: String) {
storedPhrasesRepository.updatePhraseLastSpokenTime(phraseId)
presetPhrasesRepository.updatePhraseLastSpokenTime(phraseId)
Expand All @@ -54,6 +73,7 @@ class PhrasesUseCase(
localizedUtterance = localizedUtterance,
)
}

is PresetPhrase -> {
presetPhrasesRepository.deletePhrase(phraseId = phraseId)
// add a custom phrase to "shadow" over the preset
Expand All @@ -69,24 +89,29 @@ class PhrasesUseCase(
)

}

null -> throw IllegalArgumentException("Phrase with id $phraseId not found")
}
}

override suspend fun addPhrase(localizedUtterance: LocalesWithText, parentCategoryId: String) {
if (parentCategoryId != PresetCategories.RECENTS.id) {
storedPhrasesRepository.addPhrase(PhraseDto(
phraseId = uuidProvider.randomUUIDString(),
parentCategoryId = parentCategoryId,
creationDate = dateProvider.currentTimeMillis(),
lastSpokenDate = null,
localizedUtterance = localizedUtterance,
sortOrder = legacyPhrasesRepository.getPhrasesForCategory(parentCategoryId).size
))
storedPhrasesRepository.addPhrase(
PhraseDto(
phraseId = uuidProvider.randomUUIDString(),
parentCategoryId = parentCategoryId,
creationDate = dateProvider.currentTimeMillis(),
lastSpokenDate = null,
localizedUtterance = localizedUtterance,
sortOrder = legacyPhrasesRepository.getPhrasesForCategory(parentCategoryId).size
)
)
} else {
throw Exception("The 'Recents' category is not a true category -" +
" it is a filter applied to true categories. Therefore, saving phrases from " +
"the Recents 'category' is not supported.")
throw Exception(
"The 'Recents' category is not a true category -" +
" it is a filter applied to true categories. Therefore, saving phrases from " +
"the Recents 'category' is not supported."
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import com.willowtree.vocable.customviews.CategoryButton
import com.willowtree.vocable.customviews.PointerListener
import com.willowtree.vocable.databinding.CategoriesFragmentBinding
import com.willowtree.vocable.databinding.CategoryButtonBinding
import org.koin.androidx.viewmodel.ViewModelOwner
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.activityViewModel

class CategoriesFragment : BaseFragment<CategoriesFragmentBinding>() {

Expand All @@ -35,9 +34,7 @@ class CategoriesFragment : BaseFragment<CategoriesFragmentBinding>() {
override val bindingInflater: BindingInflater<CategoriesFragmentBinding> =
CategoriesFragmentBinding::inflate

private val viewModel: PresetsViewModel by viewModel(owner = {
ViewModelOwner.from(requireActivity())
})
private val viewModel: PresetsViewModel by activityViewModel()
private val allViews = mutableListOf<View>()
private var maxCategories = 1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ import com.willowtree.vocable.R
import com.willowtree.vocable.databinding.FragmentPhrasesBinding
import com.willowtree.vocable.presets.adapter.PhraseAdapter
import com.willowtree.vocable.utils.ItemOffsetDecoration
import org.koin.androidx.viewmodel.ViewModelOwner
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.activityViewModel

class PhrasesFragment : BaseFragment<FragmentPhrasesBinding>() {
private val presetsViewModel: PresetsViewModel by viewModel(owner = {
ViewModelOwner.from(requireActivity())
})
private val presetsViewModel: PresetsViewModel by activityViewModel()

companion object {
private const val KEY_PHRASES = "KEY_PHRASES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import com.willowtree.vocable.databinding.FragmentPresetsBinding
import com.willowtree.vocable.utils.SpokenText
import com.willowtree.vocable.utils.VocableFragmentStateAdapter
import com.willowtree.vocable.utils.VocableTextToSpeech
import org.koin.androidx.viewmodel.ViewModelOwner
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.activityViewModel

class PresetsFragment : BaseFragment<FragmentPresetsBinding>() {

Expand All @@ -34,9 +33,7 @@ class PresetsFragment : BaseFragment<FragmentPresetsBinding>() {
private var isPortraitMode = true
private var isTabletMode = false

private val presetsViewModel: PresetsViewModel by viewModel(owner = {
ViewModelOwner.from(requireActivity())
})
private val presetsViewModel: PresetsViewModel by activityViewModel()
private lateinit var categoriesAdapter: CategoriesPagerAdapter
private lateinit var phrasesAdapter: PhrasesPagerAdapter

Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/com/willowtree/vocable/room/PhraseDao.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.willowtree.vocable.room

import androidx.room.*
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow

@Dao
interface PhraseDao {
Expand All @@ -26,9 +32,15 @@ interface PhraseDao {
@Query("SELECT * FROM Phrase WHERE last_spoken_date IS NOT NULL ORDER BY last_spoken_date DESC LIMIT 8")
suspend fun getRecentPhrases(): List<PhraseDto>

@Query("SELECT * FROM Phrase WHERE last_spoken_date IS NOT NULL ORDER BY last_spoken_date DESC LIMIT 8")
fun getRecentPhrasesFlow(): Flow<List<PhraseDto>>

@Query("SELECT * FROM Phrase WHERE parent_category_id == :categoryId")
suspend fun getPhrasesForCategory(categoryId: String): List<PhraseDto>

@Query("SELECT * FROM Phrase WHERE parent_category_id == :categoryId")
fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<PhraseDto>>

@Query("SELECT * FROM Phrase WHERE phrase_id == :phraseId")
suspend fun getPhrase(phraseId: String): PhraseDto?
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow

@Dao
interface PresetPhrasesDao {
Expand All @@ -21,9 +22,15 @@ interface PresetPhrasesDao {
@Query("SELECT * FROM PresetPhrase WHERE last_spoken_date IS NOT NULL ORDER BY last_spoken_date DESC LIMIT 8")
suspend fun getRecentPhrases(): List<PresetPhraseDto>

@Query("SELECT * FROM PresetPhrase WHERE last_spoken_date IS NOT NULL ORDER BY last_spoken_date DESC LIMIT 8")
fun getRecentPhrasesFlow(): Flow<List<PresetPhraseDto>>

@Query("SELECT * FROM PresetPhrase WHERE parent_category_id = :categoryId")
suspend fun getPhrasesForCategory(categoryId: String): List<PresetPhraseDto>

@Query("SELECT * FROM PresetPhrase WHERE parent_category_id = :categoryId")
fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<PresetPhraseDto>>

@Query("SELECT * FROM PresetPhrase WHERE phrase_id = :phraseId")
suspend fun getPhrase(phraseId: String): PresetPhraseDto?

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.willowtree.vocable.room

import com.willowtree.vocable.presets.PresetPhrase
import kotlinx.coroutines.flow.Flow

interface PresetPhrasesRepository {
suspend fun populateDatabase()
suspend fun getAllPresetPhrases(): List<PresetPhrase>
suspend fun updatePhraseLastSpokenTime(phraseId: String)
suspend fun getRecentPhrases() : List<PresetPhrase>
fun getRecentPhrasesFlow(): Flow<List<PresetPhrase>>
suspend fun getPhrasesForCategory(categoryId: String): List<PresetPhrase>
fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<PresetPhrase>>
suspend fun getPhrase(phraseId: String): PresetPhrase?
suspend fun deletePhrase(phraseId: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.willowtree.vocable.presets.PresetCategories
import com.willowtree.vocable.presets.PresetPhrase
import com.willowtree.vocable.presets.asPhrase
import com.willowtree.vocable.utils.DateProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.koin.core.context.GlobalContext.get
Expand Down Expand Up @@ -37,7 +39,12 @@ class RoomPresetPhrasesRepository(
override suspend fun getRecentPhrases(): List<PresetPhrase> {
return presetPhrasesDao.getRecentPhrases()
.filterDeletedPresets()
.map { it.asPhrase()}
.map { it.asPhrase() }
}

override fun getRecentPhrasesFlow(): Flow<List<PresetPhrase>> {
return presetPhrasesDao.getRecentPhrasesFlow()
.map { phraseList -> phraseList.filterDeletedPresets().map { it.asPhrase() } }
}

override suspend fun getPhrasesForCategory(categoryId: String): List<PresetPhrase> {
Expand All @@ -46,6 +53,11 @@ class RoomPresetPhrasesRepository(
.map { it.asPhrase() }
}

override fun getPhrasesForCategoryFlow(categoryId: String): Flow<List<PresetPhrase>> {
return presetPhrasesDao.getPhrasesForCategoryFlow(categoryId)
.map { phraseList -> phraseList.filterDeletedPresets().map { it.asPhrase() } }
}

override suspend fun getPhrase(phraseId: String): PresetPhrase? {
return presetPhrasesDao.getPhrase(phraseId)?.asPhrase()
}
Expand All @@ -54,7 +66,7 @@ class RoomPresetPhrasesRepository(
presetPhrasesDao.deletePhrase(phraseId, deleted = true)
}

private fun List<PresetPhraseDto>.filterDeletedPresets() : List<PresetPhraseDto> {
private fun List<PresetPhraseDto>.filterDeletedPresets(): List<PresetPhraseDto> {
return filterNot { it.deleted }
}

Expand Down
Loading

0 comments on commit c6da557

Please sign in to comment.