diff --git a/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseActivity.kt b/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseActivity.kt index 9a2f42dc..3de8659a 100644 --- a/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseActivity.kt +++ b/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseActivity.kt @@ -22,17 +22,13 @@ package ca.rmen.android.poetassistant.about import android.content.Context import android.content.Intent import android.os.Bundle -import android.util.Log import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.annotation.WorkerThread +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.collectAsState import ca.rmen.android.poetassistant.Constants -import ca.rmen.android.poetassistant.dagger.DaggerHelper import ca.rmen.android.poetassistant.theme.AppTheme -import java.io.BufferedReader -import java.io.IOException -import java.io.InputStreamReader class LicenseActivity : AppCompatActivity() { companion object { @@ -52,38 +48,20 @@ class LicenseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + val licenseViewModel: LicenseViewModel by viewModels() val title = intent.getStringExtra(EXTRA_TITLE)!! val licenseFile = intent.getStringExtra(EXTRA_LICENSE_TEXT_ASSET_FILE)!! - val threading = DaggerHelper.getMainScreenComponent(this).getThreading() - threading.execute( - { readFile(licenseFile) }, - { - setContent { - AppTheme { - LicenseScreen( - title = title, - licenseText = it, - onBack = onBackPressedDispatcher::onBackPressed - ) - } - } - }) - } - - @WorkerThread - private fun readFile(fileName: String): String { - - try { - BufferedReader(InputStreamReader(assets.open(fileName))).use { - val builder = StringBuilder() - it.forEachLine { line -> - builder.append(line).append('\n') - } - return builder.toString() + licenseViewModel.readLicenseText(licenseFile) + setContent { + val licenseText = licenseViewModel.licenseText.collectAsState() + AppTheme { + LicenseScreen( + title = title, + licenseText = licenseText.value, + onBack = onBackPressedDispatcher::onBackPressed + ) } - } catch (e: IOException) { - Log.e(TAG, "Couldn't read license file $fileName: ${e.message}", e) - return "" } } + } diff --git a/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseViewModel.kt b/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseViewModel.kt new file mode 100644 index 00000000..de71f339 --- /dev/null +++ b/app/src/main/kotlin/ca/rmen/android/poetassistant/about/LicenseViewModel.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 - present Carmen Alvarez + * + * This file is part of Poet Assistant. + * + * Poet Assistant is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Poet Assistant is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Poet Assistant. If not, see . + */ +package ca.rmen.android.poetassistant.about + +import android.app.Application +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import ca.rmen.android.poetassistant.Constants +import ca.rmen.android.poetassistant.dagger.DaggerHelper +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import java.io.IOException +import javax.inject.Inject + +class LicenseViewModel(private val application: Application) : + AndroidViewModel(application) { + + companion object { + private val TAG = Constants.TAG + LicenseViewModel::class.java.simpleName + } + + init { + DaggerHelper.getMainScreenComponent(application).inject(this) + } + + @Inject + lateinit var ioDispatcher: CoroutineDispatcher + + private val _licenseText = MutableStateFlow("") + val licenseText = _licenseText.asStateFlow() + + fun readLicenseText(fileName: String) { + viewModelScope.launch(ioDispatcher) { + val contents = readFile(fileName) + _licenseText.value = contents + } + } + + private fun readFile(fileName: String): String { + try { + return application.assets.open(fileName).bufferedReader().use { it.readText() } + } catch (e: IOException) { + Log.e(TAG, "Couldn't read license file $fileName: ${e.message}", e) + return "" + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/AppComponent.kt b/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/AppComponent.kt index cf22b1a2..e7cbcd14 100644 --- a/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/AppComponent.kt +++ b/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/AppComponent.kt @@ -22,6 +22,7 @@ package ca.rmen.android.poetassistant.dagger import ca.rmen.android.poetassistant.Favorites import ca.rmen.android.poetassistant.PoemAudioExport import ca.rmen.android.poetassistant.Threading +import ca.rmen.android.poetassistant.about.LicenseViewModel import ca.rmen.android.poetassistant.main.MainActivity import ca.rmen.android.poetassistant.main.dictionaries.ResultListAdapterFactory import ca.rmen.android.poetassistant.main.dictionaries.ResultListHeaderViewModel @@ -48,6 +49,7 @@ import ca.rmen.android.poetassistant.wotd.WotdEntryViewModel import ca.rmen.android.poetassistant.wotd.WotdLiveData import dagger.Component import dagger.Subcomponent +import kotlinx.coroutines.CoroutineDispatcher import javax.inject.Singleton @Singleton @@ -64,6 +66,7 @@ interface AppComponent { fun getFavorites(): Favorites fun getSettingsPrefs(): SettingsPrefs fun getThreading(): Threading + fun getIODispatcher(): CoroutineDispatcher fun getResultListAdapterFactory() : ResultListAdapterFactory } @@ -75,6 +78,7 @@ interface AppComponent { fun injectDict(resultListViewModel: ResultListViewModel) fun inject(rtEntry: RTEntryViewModel) fun inject(resultListHeaderViewModel: ResultListHeaderViewModel) + fun inject(licenseViewModel: LicenseViewModel) fun inject(readerViewModel: ReaderViewModel) fun inject(poemAudioExport: PoemAudioExport) fun inject(rhymerLiveData: RhymerLiveData) diff --git a/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/ThreadingModule.kt b/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/ThreadingModule.kt index e87dc44c..e0c4808e 100644 --- a/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/ThreadingModule.kt +++ b/app/src/main/kotlin/ca/rmen/android/poetassistant/dagger/ThreadingModule.kt @@ -23,6 +23,7 @@ import ca.rmen.android.poetassistant.CoroutineThreading import ca.rmen.android.poetassistant.Threading import dagger.Module import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import javax.inject.Singleton @@ -31,4 +32,9 @@ class ThreadingModule { @Provides @Singleton fun providesThreading() : Threading = CoroutineThreading(Dispatchers.Default, Dispatchers.Main) + + + @Provides + @Singleton + fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO } \ No newline at end of file diff --git a/app/src/sharedTest/kotlin/ca/rmen/android/poetassistant/dagger/TestThreadingModule.kt b/app/src/sharedTest/kotlin/ca/rmen/android/poetassistant/dagger/TestThreadingModule.kt index 191ca9fa..f850fafd 100644 --- a/app/src/sharedTest/kotlin/ca/rmen/android/poetassistant/dagger/TestThreadingModule.kt +++ b/app/src/sharedTest/kotlin/ca/rmen/android/poetassistant/dagger/TestThreadingModule.kt @@ -23,6 +23,9 @@ import ca.rmen.android.poetassistant.InstrumentationThreading import ca.rmen.android.poetassistant.Threading import dagger.Module import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import javax.inject.Singleton @Module @@ -31,4 +34,9 @@ class TestThreadingModule { @Provides @Singleton fun providesThreading(): Threading = InstrumentationThreading() + + @OptIn(ExperimentalCoroutinesApi::class) + @Provides + @Singleton + fun providesIODispatcher(): CoroutineDispatcher = UnconfinedTestDispatcher() } diff --git a/app/src/test/kotlin/ca/rmen/android/poetassistant/dagger/JunitThreadingModule.kt b/app/src/test/kotlin/ca/rmen/android/poetassistant/dagger/JunitThreadingModule.kt index 5f87eb6e..1ae67abf 100644 --- a/app/src/test/kotlin/ca/rmen/android/poetassistant/dagger/JunitThreadingModule.kt +++ b/app/src/test/kotlin/ca/rmen/android/poetassistant/dagger/JunitThreadingModule.kt @@ -23,6 +23,9 @@ import ca.rmen.android.poetassistant.JunitThreading import ca.rmen.android.poetassistant.Threading import dagger.Module import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import javax.inject.Singleton @Module @@ -30,4 +33,9 @@ class JunitThreadingModule { @Provides @Singleton fun providesThreading() : Threading = JunitThreading() + + @OptIn(ExperimentalCoroutinesApi::class) + @Provides + @Singleton + fun providesIODispatcher(): CoroutineDispatcher = UnconfinedTestDispatcher() }