From 2bbdd3f0440d543686a30bf17d3617c531c09969 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 25 Jul 2022 17:47:35 +0300 Subject: [PATCH] Migrate from Koin to Dagger/Hilt --- app/build.gradle | 14 +- .../org/koitharu/kotatsu/Instrumentation.kt | 2 +- .../kotatsu/core/db/MangaDatabaseTest.kt | 4 +- .../kotatsu/core/os/ShortcutsUpdaterTest.kt | 32 ++-- .../settings/backup/AppBackupAgentTest.kt | 42 +++-- .../kotatsu/tracker/domain/TrackerTest.kt | 35 ++-- app/src/main/AndroidManifest.xml | 4 + .../java/org/koitharu/kotatsu/KotatsuApp.kt | 92 ++++------ .../base/domain/MangaDataRepository.kt | 84 ++++++++- .../kotatsu/base/domain/MangaUtils.kt | 76 --------- .../koitharu/kotatsu/base/ui/BaseActivity.kt | 15 +- .../kotatsu/base/ui/BaseBottomSheet.kt | 6 +- .../kotatsu/base/ui/BasePreferenceFragment.kt | 11 +- .../kotatsu/base/ui/CoroutineIntentService.kt | 2 +- .../base/ui/util/ActivityRecreationHandle.kt | 7 +- .../base/ui/util/BaseActivityEntryPoint.kt | 18 ++ .../kotatsu/bookmarks/BookmarksModule.kt | 14 -- .../bookmarks/domain/BookmarksRepository.kt | 5 +- .../kotatsu/bookmarks/ui/BookmarksActivity.kt | 6 +- .../kotatsu/bookmarks/ui/BookmarksFragment.kt | 14 +- .../bookmarks/ui/BookmarksViewModel.kt | 10 +- .../browser/cloudflare/CloudFlareDialog.kt | 14 +- .../org/koitharu/kotatsu/core/AppModule.kt | 160 ++++++++++++++++++ .../kotatsu/core/backup/BackupRepository.kt | 5 +- .../kotatsu/core/db/DatabaseModule.kt | 9 - .../kotatsu/core/github/AppUpdateModule.kt | 9 - .../core/github/AppUpdateRepository.kt | 8 +- .../kotatsu/core/network/AndroidCookieJar.kt | 11 +- .../kotatsu/core/network/NetworkModule.kt | 30 ---- .../kotatsu/core/os/ShortcutsUpdater.kt | 21 ++- .../core/parser/MangaLoaderContextImpl.kt | 16 +- .../kotatsu/core/parser/MangaRepository.kt | 20 ++- .../core/parser/favicon/FaviconFetcher.kt | 10 +- .../kotatsu/core/prefs/AppSettings.kt | 14 +- .../org/koitharu/kotatsu/core/ui/uiModule.kt | 53 ------ .../koitharu/kotatsu/details/DetailsModule.kt | 13 -- .../kotatsu/details/ui/ChaptersFragment.kt | 14 +- .../kotatsu/details/ui/DetailsActivity.kt | 42 +++-- .../kotatsu/details/ui/DetailsFragment.kt | 28 +-- .../kotatsu/details/ui/DetailsViewModel.kt | 28 ++- .../details/ui/MangaDetailsDelegate.kt | 7 +- .../scrobbling/ScrobblingInfoBottomSheet.kt | 16 +- .../download/domain/DownloadManager.kt | 56 +++--- .../kotatsu/download/ui/DownloadsActivity.kt | 16 +- .../download/ui/service/DownloadService.kt | 65 +++---- .../koitharu/kotatsu/explore/ExploreModule.kt | 14 -- .../explore/domain/ExploreRepository.kt | 8 +- .../kotatsu/explore/ui/ExploreFragment.kt | 19 ++- .../kotatsu/explore/ui/ExploreViewModel.kt | 7 +- .../kotatsu/favourites/FavouritesModule.kt | 24 --- .../favourites/domain/FavouritesRepository.kt | 5 +- .../favourites/ui/FavouritesActivity.kt | 4 +- .../categories/FavouriteCategoriesActivity.kt | 17 +- .../FavouritesCategoriesViewModel.kt | 9 +- .../edit/FavouritesCategoryEditActivity.kt | 22 ++- .../edit/FavouritesCategoryEditViewModel.kt | 15 +- .../select/FavouriteCategoriesBottomSheet.kt | 13 +- .../select/MangaCategoriesViewModel.kt | 21 ++- .../ui/list/FavouritesListFragment.kt | 13 +- .../ui/list/FavouritesListViewModel.kt | 13 +- .../koitharu/kotatsu/history/HistoryModule.kt | 14 -- .../history/domain/HistoryRepository.kt | 7 +- .../kotatsu/history/ui/HistoryActivity.kt | 2 + .../kotatsu/history/ui/HistoryListFragment.kt | 9 +- .../history/ui/HistoryListViewModel.kt | 15 +- .../kotatsu/image/ui/ImageActivity.kt | 11 +- .../koitharu/kotatsu/library/LibraryModule.kt | 16 -- .../library/domain/LibraryRepository.kt | 3 +- .../kotatsu/library/ui/LibraryFragment.kt | 20 ++- .../kotatsu/library/ui/LibraryViewModel.kt | 5 +- .../LibraryCategoriesConfigSheet.kt | 6 +- .../LibraryCategoriesConfigViewModel.kt | 5 +- .../ui/config/size/LibrarySizeBottomSheet.kt | 7 +- .../kotatsu/list/ui/ListModeSelectDialog.kt | 7 +- .../kotatsu/list/ui/MangaListFragment.kt | 8 +- .../list/ui/filter/FilterBottomSheet.kt | 7 +- .../org/koitharu/kotatsu/local/LocalModule.kt | 19 --- .../kotatsu/local/data/LocalStorageManager.kt | 12 +- .../koitharu/kotatsu/local/data/PagesCache.kt | 12 +- .../local/domain/LocalMangaRepository.kt | 15 +- .../local/ui/LocalChaptersRemoveService.kt | 11 +- .../kotatsu/local/ui/LocalListFragment.kt | 4 +- .../kotatsu/local/ui/LocalListViewModel.kt | 13 +- .../org/koitharu/kotatsu/main/MainModule.kt | 29 ---- .../koitharu/kotatsu/main/ui/ExitCallback.kt | 7 +- .../koitharu/kotatsu/main/ui/MainActivity.kt | 19 +-- .../koitharu/kotatsu/main/ui/MainViewModel.kt | 5 +- .../main/ui/protect/AppProtectHelper.kt | 7 +- .../main/ui/protect/ProtectActivity.kt | 10 +- .../main/ui/protect/ProtectViewModel.kt | 7 +- .../koitharu/kotatsu/reader/ReaderModule.kt | 31 ---- .../kotatsu/reader/domain/ChaptersLoader.kt | 6 +- .../kotatsu/reader/domain/PageLoader.kt | 31 ++-- .../kotatsu/reader/ui/ChaptersBottomSheet.kt | 11 +- .../kotatsu/reader/ui/PageSaveHelper.kt | 20 +-- .../kotatsu/reader/ui/ReaderActivity.kt | 30 ++-- .../kotatsu/reader/ui/ReaderViewModel.kt | 41 +++-- .../ui/config/ReaderConfigBottomSheet.kt | 4 +- .../kotatsu/reader/ui/pager/BaseReader.kt | 6 +- .../pager/reversed/ReversedReaderFragment.kt | 18 +- .../ui/pager/standard/PagerReaderFragment.kt | 18 +- .../ui/pager/webtoon/WebtoonReaderFragment.kt | 18 +- .../ui/thumbnails/PagesThumbnailsSheet.kt | 30 +++- .../kotatsu/remotelist/RemoteListModule.kt | 20 --- .../remotelist/ui/RemoteListFragment.kt | 13 +- .../remotelist/ui/RemoteListViewModel.kt | 23 ++- .../kotatsu/scrobbling/ScrobblingModule.kt | 41 +++++ .../scrobbling/shikimori/ShikimoriModule.kt | 32 ---- .../shikimori/data/ShikimoriAuthenticator.kt | 10 +- .../shikimori/data/ShikimoriRepository.kt | 8 +- .../shikimori/data/ShikimoriStorage.kt | 8 +- .../shikimori/domain/ShikimoriScrobbler.kt | 7 +- .../shikimori/ui/ShikimoriSettingsFragment.kt | 18 +- .../ui/ShikimoriSettingsViewModel.kt | 15 +- .../selector/ScrobblingSelectorBottomSheet.kt | 20 ++- .../selector/ScrobblingSelectorViewModel.kt | 17 +- .../koitharu/kotatsu/search/SearchModule.kt | 22 --- .../search/domain/MangaSearchRepository.kt | 15 +- .../kotatsu/search/ui/MangaListActivity.kt | 17 +- .../kotatsu/search/ui/SearchActivity.kt | 6 +- .../kotatsu/search/ui/SearchFragment.kt | 13 +- .../kotatsu/search/ui/SearchViewModel.kt | 26 ++- .../search/ui/multi/MultiSearchActivity.kt | 26 ++- .../search/ui/multi/MultiSearchViewModel.kt | 20 ++- .../ui/suggestion/SearchSuggestionFragment.kt | 16 +- .../suggestion/SearchSuggestionViewModel.kt | 7 +- .../kotatsu/settings/AppUpdateChecker.kt | 5 +- .../settings/AppearanceSettingsFragment.kt | 13 +- .../settings/ContentSettingsFragment.kt | 13 +- .../settings/HistorySettingsFragment.kt | 32 ++-- .../NotificationSettingsLegacyFragment.kt | 6 +- .../kotatsu/settings/SettingsActivity.kt | 8 +- .../kotatsu/settings/SettingsModule.kt | 39 ----- .../settings/SourceSettingsFragment.kt | 12 +- .../settings/SuggestionsSettingsFragment.kt | 18 +- .../settings/TrackerSettingsFragment.kt | 18 +- .../settings/backup/BackupDialogFragment.kt | 12 +- .../kotatsu/settings/backup/BackupObserver.kt | 13 +- .../settings/backup/BackupViewModel.kt | 12 +- .../settings/backup/RestoreDialogFragment.kt | 21 ++- .../settings/backup/RestoreViewModel.kt | 22 ++- .../newsources/NewSourcesDialogFragment.kt | 14 +- .../newsources/NewSourcesViewModel.kt | 7 +- .../settings/onboard/OnboardDialogFragment.kt | 6 +- .../settings/onboard/OnboardViewModel.kt | 11 +- .../settings/protect/ProtectSetupActivity.kt | 18 +- .../settings/protect/ProtectSetupViewModel.kt | 9 +- .../sources/SourcesSettingsFragment.kt | 20 ++- .../sources/SourcesSettingsViewModel.kt | 9 +- .../sources/auth/SourceAuthActivity.kt | 14 +- .../kotatsu/settings/tools/ToolsFragment.kt | 6 +- .../kotatsu/settings/tools/ToolsViewModel.kt | 5 +- .../settings/utils/RingtonePickContract.kt | 13 +- .../utils/TagsAutoCompleteProvider.kt | 5 +- .../kotatsu/suggestions/SuggestionsModule.kt | 14 -- .../domain/SuggestionRepository.kt | 7 +- .../suggestions/ui/SuggestionsFragment.kt | 4 +- .../suggestions/ui/SuggestionsViewModel.kt | 13 +- .../suggestions/ui/SuggestionsWorker.kt | 32 ++-- .../org/koitharu/kotatsu/sync/SyncModule.kt | 20 --- .../koitharu/kotatsu/sync/data/SyncAuthApi.kt | 8 +- .../kotatsu/sync/domain/SyncController.kt | 10 +- .../kotatsu/sync/ui/SyncAuthActivity.kt | 6 +- .../kotatsu/sync/ui/SyncAuthViewModel.kt | 7 +- .../koitharu/kotatsu/sync/ui/SyncProvider.kt | 23 ++- .../koitharu/kotatsu/tracker/TrackerModule.kt | 20 --- .../kotatsu/tracker/domain/Tracker.kt | 6 +- .../tracker/domain/TrackingRepository.kt | 3 +- .../kotatsu/tracker/ui/FeedFragment.kt | 30 +++- .../kotatsu/tracker/ui/FeedViewModel.kt | 15 +- .../kotatsu/tracker/work/TrackWorker.kt | 38 +++-- .../work/TrackerNotificationChannels.kt | 8 +- .../koitharu/kotatsu/utils/ext/ViewModel.kt | 46 +++++ .../kotatsu/utils/image/CoilImageGetter.kt | 10 +- .../kotatsu/widget/AppWidgetModule.kt | 15 -- .../koitharu/kotatsu/widget/WidgetUpdater.kt | 8 +- .../widget/recent/RecentWidgetService.kt | 16 +- .../widget/shelf/ShelfConfigActivity.kt | 20 ++- .../widget/shelf/ShelfConfigViewModel.kt | 11 +- .../widget/shelf/ShelfWidgetService.kt | 18 +- build.gradle | 3 +- 181 files changed, 1751 insertions(+), 1379 deletions(-) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/base/domain/MangaUtils.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/base/ui/util/BaseActivityEntryPoint.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/bookmarks/BookmarksModule.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/ui/uiModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/details/DetailsModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/favourites/FavouritesModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/history/HistoryModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ReaderModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/remotelist/RemoteListModule.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ShikimoriModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/search/SearchModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/suggestions/SuggestionsModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/tracker/TrackerModule.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewModel.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/widget/AppWidgetModule.kt diff --git a/app/build.gradle b/app/build.gradle index 34cb14f91..7d1fea5ef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,7 @@ plugins { id 'kotlin-android' id 'kotlin-kapt' id 'kotlin-parcelize' + id 'dagger.hilt.android.plugin' } android { @@ -29,7 +30,7 @@ android { buildConfigField 'String', 'SHIKIMORI_CLIENT_ID', "\"${localProperty('shikimori.clientId')}\"" buildConfigField 'String', 'SHIKIMORI_CLIENT_SECRET', "\"${localProperty('shikimori.clientSecret')}\"" - if (currentBranch() == "feature/nextgen") { + if (currentBranch().startsWith("feature/nextgen")) { applicationIdSuffix = '.next' } } @@ -118,7 +119,11 @@ dependencies { implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2' - implementation 'io.insert-koin:koin-android:3.2.0' + implementation "com.google.dagger:hilt-android:2.42" + kapt "com.google.dagger:hilt-compiler:2.42" + implementation 'androidx.hilt:hilt-work:1.0.0' + kapt 'androidx.hilt:hilt-compiler:1.0.0' + implementation 'io.coil-kt:coil-base:2.1.0' implementation 'io.coil-kt:coil-svg:2.1.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' @@ -139,9 +144,10 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3' androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' - androidTestImplementation 'io.insert-koin:koin-test:3.2.0' - androidTestImplementation 'io.insert-koin:koin-test-junit4:3.2.0' androidTestImplementation 'androidx.room:room-testing:2.4.2' androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.13.0' + + androidTestImplementation 'com.google.dagger:hilt-android-testing:2.42' + kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.42' } diff --git a/app/src/androidTest/java/org/koitharu/kotatsu/Instrumentation.kt b/app/src/androidTest/java/org/koitharu/kotatsu/Instrumentation.kt index b9ef582c1..dbf4ec642 100644 --- a/app/src/androidTest/java/org/koitharu/kotatsu/Instrumentation.kt +++ b/app/src/androidTest/java/org/koitharu/kotatsu/Instrumentation.kt @@ -6,4 +6,4 @@ import kotlin.coroutines.suspendCoroutine suspend fun Instrumentation.awaitForIdle() = suspendCoroutine { cont -> waitForIdle { cont.resume(Unit) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/org/koitharu/kotatsu/core/db/MangaDatabaseTest.kt b/app/src/androidTest/java/org/koitharu/kotatsu/core/db/MangaDatabaseTest.kt index 23c8b9796..b7e4a07f5 100644 --- a/app/src/androidTest/java/org/koitharu/kotatsu/core/db/MangaDatabaseTest.kt +++ b/app/src/androidTest/java/org/koitharu/kotatsu/core/db/MangaDatabaseTest.kt @@ -3,10 +3,10 @@ package org.koitharu.kotatsu.core.db import androidx.room.testing.MigrationTestHelper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) class MangaDatabaseTest { @@ -37,7 +37,7 @@ class MangaDatabaseTest { TEST_DB, migration.endVersion, true, - migration + migration, ).close() } } diff --git a/app/src/androidTest/java/org/koitharu/kotatsu/core/os/ShortcutsUpdaterTest.kt b/app/src/androidTest/java/org/koitharu/kotatsu/core/os/ShortcutsUpdaterTest.kt index ec4c04edc..4b1784bed 100644 --- a/app/src/androidTest/java/org/koitharu/kotatsu/core/os/ShortcutsUpdaterTest.kt +++ b/app/src/androidTest/java/org/koitharu/kotatsu/core/os/ShortcutsUpdaterTest.kt @@ -6,28 +6,40 @@ import android.os.Build import androidx.core.content.getSystemService import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import javax.inject.Inject import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.koin.test.KoinTest -import org.koin.test.inject import org.koitharu.kotatsu.SampleData import org.koitharu.kotatsu.awaitForIdle import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.history.domain.HistoryRepository -import kotlin.test.assertEquals -import kotlin.test.assertTrue +@HiltAndroidTest @RunWith(AndroidJUnit4::class) -class ShortcutsUpdaterTest : KoinTest { +class ShortcutsUpdaterTest { - private val historyRepository by inject() - private val shortcutsUpdater by inject() - private val database by inject() + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var historyRepository: HistoryRepository + + @Inject + lateinit var shortcutsUpdater: ShortcutsUpdater + + @Inject + lateinit var database: MangaDatabase @Before fun setUp() { + hiltRule.inject() database.clearAllTables() } @@ -43,7 +55,7 @@ class ShortcutsUpdaterTest : KoinTest { chapterId = SampleData.chapter.id, page = 4, scroll = 2, - percent = 0.3f + percent = 0.3f, ) awaitUpdate() @@ -62,4 +74,4 @@ class ShortcutsUpdaterTest : KoinTest { instrumentation.awaitForIdle() shortcutsUpdater.await() } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/org/koitharu/kotatsu/settings/backup/AppBackupAgentTest.kt b/app/src/androidTest/java/org/koitharu/kotatsu/settings/backup/AppBackupAgentTest.kt index 57dd36fd0..f4448baa4 100644 --- a/app/src/androidTest/java/org/koitharu/kotatsu/settings/backup/AppBackupAgentTest.kt +++ b/app/src/androidTest/java/org/koitharu/kotatsu/settings/backup/AppBackupAgentTest.kt @@ -3,33 +3,46 @@ package org.koitharu.kotatsu.settings.backup import android.content.res.AssetManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import java.io.File +import javax.inject.Inject import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest +import org.junit.Assert.* import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.koin.test.KoinTest -import org.koin.test.get -import org.koin.test.inject import org.koitharu.kotatsu.SampleData import org.koitharu.kotatsu.core.backup.BackupRepository import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.toMangaTags import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.domain.HistoryRepository -import java.io.File -import kotlin.test.* +@HiltAndroidTest @RunWith(AndroidJUnit4::class) -class AppBackupAgentTest : KoinTest { +class AppBackupAgentTest { - private val historyRepository by inject() - private val favouritesRepository by inject() - private val backupRepository by inject() - private val database by inject() + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var historyRepository: HistoryRepository + + @Inject + lateinit var favouritesRepository: FavouritesRepository + + @Inject + lateinit var backupRepository: BackupRepository + + @Inject + lateinit var database: MangaDatabase @Before fun setUp() { + hiltRule.inject() database.clearAllTables() } @@ -51,7 +64,10 @@ class AppBackupAgentTest : KoinTest { val history = checkNotNull(historyRepository.getOne(SampleData.manga)) val agent = AppBackupAgent() - val backup = agent.createBackupFile(get(), backupRepository) + val backup = agent.createBackupFile( + context = InstrumentationRegistry.getInstrumentation().targetContext, + repository = backupRepository, + ) database.clearAllTables() assertTrue(favouritesRepository.getAllManga().isEmpty()) @@ -63,10 +79,10 @@ class AppBackupAgentTest : KoinTest { assertEquals(category, favouritesRepository.getCategory(category.id)) assertEquals(history, historyRepository.getOne(SampleData.manga)) - assertContentEquals(listOf(SampleData.manga), favouritesRepository.getManga(category.id)) + assertEquals(listOf(SampleData.manga), favouritesRepository.getManga(category.id)) val allTags = database.tagsDao.findTags(SampleData.tag.source.name).toMangaTags() - assertContains(allTags, SampleData.tag) + assertTrue(SampleData.tag in allTags) } @Test diff --git a/app/src/androidTest/java/org/koitharu/kotatsu/tracker/domain/TrackerTest.kt b/app/src/androidTest/java/org/koitharu/kotatsu/tracker/domain/TrackerTest.kt index a1ea460f6..37b5ebf02 100644 --- a/app/src/androidTest/java/org/koitharu/kotatsu/tracker/domain/TrackerTest.kt +++ b/app/src/androidTest/java/org/koitharu/kotatsu/tracker/domain/TrackerTest.kt @@ -1,24 +1,39 @@ package org.koitharu.kotatsu.tracker.domain import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import javax.inject.Inject +import junit.framework.TestCase.* import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.koin.test.KoinTest -import org.koin.test.inject import org.koitharu.kotatsu.SampleData import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.parsers.model.Manga -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue +@HiltAndroidTest @RunWith(AndroidJUnit4::class) -class TrackerTest : KoinTest { +class TrackerTest { - private val repository by inject() - private val dataRepository by inject() - private val tracker by inject() + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var repository: TrackingRepository + + @Inject + lateinit var dataRepository: MangaDataRepository + + @Inject + lateinit var tracker: Tracker + + @Before + fun setUp() { + hiltRule.inject() + } @Test fun noUpdates() = runTest { @@ -180,4 +195,4 @@ class TrackerTest : KoinTest { dataRepository.storeManga(manga) return manga } -} \ No newline at end of file +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 880e595c3..2e2325290 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -204,6 +204,10 @@ android:exported="false" android:label="@string/history" android:syncable="true" /> + + + @Inject + lateinit var activityLifecycleCallbacks: Set<@JvmSuppressWildcards ActivityLifecycleCallbacks> + + @Inject + lateinit var database: MangaDatabase + + @Inject + lateinit var settings: AppSettings + + @Inject + lateinit var workerFactory: HiltWorkerFactory override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) { enableStrictMode() } - initKoin() - AppCompatDelegate.setDefaultNightMode(get().theme) + AppCompatDelegate.setDefaultNightMode(settings.theme) setupActivityLifecycleCallbacks() setupDatabaseObservers() } - private fun initKoin() { - startKoin { - androidContext(this@KotatsuApp) - modules( - networkModule, - databaseModule, - appUpdateModule, - uiModule, - mainModule, - searchModule, - localModule, - favouritesModule, - historyModule, - remoteListModule, - detailsModule, - trackerModule, - settingsModule, - readerModule, - appWidgetModule, - suggestionsModule, - syncModule, - shikimoriModule, - bookmarksModule, - libraryModule, - exploreModule, - ) - } - } - override fun attachBaseContext(base: Context?) { super.attachBaseContext(base) initAcra { @@ -115,18 +80,21 @@ class KotatsuApp : Application() { } } + override fun getWorkManagerConfiguration(): Configuration { + return Configuration.Builder() + .setWorkerFactory(workerFactory) + .build() + } + private fun setupDatabaseObservers() { - val observers = getKoin().getAll() - val database = get() val tracker = database.invalidationTracker - observers.forEach { + databaseObservers.forEach { tracker.addObserver(it) } } private fun setupActivityLifecycleCallbacks() { - val callbacks = getKoin().getAll() - callbacks.forEach { + activityLifecycleCallbacks.forEach { registerActivityLifecycleCallbacks(it) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaDataRepository.kt b/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaDataRepository.kt index 179d87868..9bc5c08dc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaDataRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaDataRepository.kt @@ -1,14 +1,35 @@ package org.koitharu.kotatsu.base.domain +import android.graphics.BitmapFactory +import android.net.Uri +import android.util.Size import androidx.room.withTransaction +import java.io.File +import java.io.InputStream +import java.util.zip.ZipFile +import javax.inject.Inject +import kotlin.math.roundToInt +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runInterruptible +import okhttp3.OkHttpClient +import okhttp3.Request import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.* +import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.util.await -class MangaDataRepository(private val db: MangaDatabase) { +private const val MIN_WEBTOON_RATIO = 2 + +class MangaDataRepository @Inject constructor( + private val okHttpClient: OkHttpClient, + private val db: MangaDatabase, +) { suspend fun savePreferences(manga: Manga, mode: ReaderMode) { val tags = manga.tags.toEntities() @@ -18,8 +39,8 @@ class MangaDataRepository(private val db: MangaDatabase) { db.preferencesDao.upsert( MangaPrefsEntity( mangaId = manga.id, - mode = mode.id - ) + mode = mode.id, + ), ) } } @@ -49,4 +70,59 @@ class MangaDataRepository(private val db: MangaDatabase) { suspend fun findTags(source: MangaSource): Set { return db.tagsDao.findTags(source.name).toMangaTags() } -} \ No newline at end of file + + /** + * Automatic determine type of manga by page size + * @return ReaderMode.WEBTOON if page is wide + */ + suspend fun determineMangaIsWebtoon(repository: MangaRepository, pages: List): Boolean { + val pageIndex = (pages.size * 0.3).roundToInt() + val page = requireNotNull(pages.getOrNull(pageIndex)) { "No pages" } + val url = repository.getPageUrl(page) + val uri = Uri.parse(url) + val size = if (uri.scheme == "cbz") { + runInterruptible(Dispatchers.IO) { + val zip = ZipFile(uri.schemeSpecificPart) + val entry = zip.getEntry(uri.fragment) + zip.getInputStream(entry).use { + getBitmapSize(it) + } + } + } else { + val request = Request.Builder() + .url(url) + .get() + .header(CommonHeaders.REFERER, page.referer) + .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) + .build() + okHttpClient.newCall(request).await().use { + runInterruptible(Dispatchers.IO) { + getBitmapSize(it.body?.byteStream()) + } + } + } + return size.width * MIN_WEBTOON_RATIO < size.height + } + + companion object { + + suspend fun getImageMimeType(file: File): String? = runInterruptible(Dispatchers.IO) { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeFile(file.path, options)?.recycle() + options.outMimeType + } + + private fun getBitmapSize(input: InputStream?): Size { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeStream(input, null, options)?.recycle() + val imageHeight: Int = options.outHeight + val imageWidth: Int = options.outWidth + check(imageHeight > 0 && imageWidth > 0) + return Size(imageWidth, imageHeight) + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaUtils.kt b/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaUtils.kt deleted file mode 100644 index b3b32dc1f..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/base/domain/MangaUtils.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.koitharu.kotatsu.base.domain - -import android.graphics.BitmapFactory -import android.net.Uri -import android.util.Size -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runInterruptible -import okhttp3.OkHttpClient -import okhttp3.Request -import org.koin.core.component.KoinComponent -import org.koin.core.component.get -import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.util.await -import java.io.File -import java.io.InputStream -import java.util.zip.ZipFile -import kotlin.math.roundToInt - -object MangaUtils : KoinComponent { - - private const val MIN_WEBTOON_RATIO = 2 - - /** - * Automatic determine type of manga by page size - * @return ReaderMode.WEBTOON if page is wide - */ - suspend fun determineMangaIsWebtoon(pages: List): Boolean { - val pageIndex = (pages.size * 0.3).roundToInt() - val page = requireNotNull(pages.getOrNull(pageIndex)) { "No pages" } - val url = MangaRepository(page.source).getPageUrl(page) - val uri = Uri.parse(url) - val size = if (uri.scheme == "cbz") { - runInterruptible(Dispatchers.IO) { - val zip = ZipFile(uri.schemeSpecificPart) - val entry = zip.getEntry(uri.fragment) - zip.getInputStream(entry).use { - getBitmapSize(it) - } - } - } else { - val request = Request.Builder() - .url(url) - .get() - .header(CommonHeaders.REFERER, page.referer) - .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) - .build() - get().newCall(request).await().use { - runInterruptible(Dispatchers.IO) { - getBitmapSize(it.body?.byteStream()) - } - } - } - return size.width * MIN_WEBTOON_RATIO < size.height - } - - suspend fun getImageMimeType(file: File): String? = runInterruptible(Dispatchers.IO) { - val options = BitmapFactory.Options().apply { - inJustDecodeBounds = true - } - BitmapFactory.decodeFile(file.path, options)?.recycle() - options.outMimeType - } - - private fun getBitmapSize(input: InputStream?): Size { - val options = BitmapFactory.Options().apply { - inJustDecodeBounds = true - } - BitmapFactory.decodeStream(input, null, options)?.recycle() - val imageHeight: Int = options.outHeight - val imageWidth: Int = options.outWidth - check(imageHeight > 0 && imageWidth > 0) - return Size(imageWidth, imageHeight) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt index f97708e00..e83ec7840 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt @@ -18,19 +18,24 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updateLayoutParams import androidx.viewbinding.ViewBinding -import org.koin.android.ext.android.get +import dagger.hilt.android.EntryPointAccessors +import javax.inject.Inject import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.util.ActionModeDelegate +import org.koitharu.kotatsu.base.ui.util.BaseActivityEntryPoint import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate +import org.koitharu.kotatsu.base.ui.util.inject import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.settings.SettingsActivity abstract class BaseActivity : AppCompatActivity(), WindowInsetsDelegate.WindowInsetsListener { + @Inject + lateinit var settings: AppSettings + protected lateinit var binding: B private set @@ -43,7 +48,7 @@ abstract class BaseActivity : val actionModeDelegate = ActionModeDelegate() override fun onCreate(savedInstanceState: Bundle?) { - val settings = get() + EntryPointAccessors.fromApplication(this, BaseActivityEntryPoint::class.java).inject(this) val isAmoled = settings.isAmoledTheme val isDynamic = settings.isDynamicTheme // TODO support DialogWhenLarge theme @@ -97,7 +102,7 @@ abstract class BaseActivity : protected fun isDarkAmoledTheme(): Boolean { val uiMode = resources.configuration.uiMode val isNight = uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES - return isNight && get().isAmoledTheme + return isNight && settings.isAmoledTheme } @CallSuper @@ -129,4 +134,4 @@ abstract class BaseActivity : super.onBackPressed() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt index c8c4051b3..1bba104b8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt @@ -9,13 +9,13 @@ import android.view.ViewGroup import android.view.ViewGroup.LayoutParams import androidx.core.view.updateLayoutParams import androidx.viewbinding.ViewBinding +import com.google.android.material.R as materialR import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.dialog.AppBottomSheetDialog import org.koitharu.kotatsu.utils.ext.displayCompat -import com.google.android.material.R as materialR abstract class BaseBottomSheet : BottomSheetDialogFragment() { @@ -30,7 +30,7 @@ abstract class BaseBottomSheet : BottomSheetDialogFragment() { final override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { val binding = onInflateView(inflater, container) viewBinding = binding @@ -83,4 +83,4 @@ abstract class BaseBottomSheet : BottomSheetDialogFragment() { } b.isDraggable = !isLocked } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt index 7db0f6e22..e71dcd005 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BasePreferenceFragment.kt @@ -8,18 +8,21 @@ import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.preference.PreferenceFragmentCompat import androidx.recyclerview.widget.RecyclerView -import org.koin.android.ext.android.inject +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.settings.SettingsHeadersFragment +@AndroidEntryPoint abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : PreferenceFragmentCompat(), WindowInsetsDelegate.WindowInsetsListener, RecyclerViewOwner { - protected val settings by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var settings: AppSettings @Suppress("LeakingThis") protected val insetsDelegate = WindowInsetsDelegate(this) @@ -48,7 +51,7 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : @CallSuper override fun onWindowInsetsChanged(insets: Insets) { listView.updatePadding( - bottom = insets.bottom + bottom = insets.bottom, ) } @@ -57,4 +60,4 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : (parentFragment as? SettingsHeadersFragment)?.setTitle(title) ?: activity?.setTitle(title) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/CoroutineIntentService.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/CoroutineIntentService.kt index 241d13f94..10f06f68d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/CoroutineIntentService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/CoroutineIntentService.kt @@ -34,4 +34,4 @@ abstract class CoroutineIntentService : BaseService() { } protected abstract suspend fun processIntent(intent: Intent?) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/ActivityRecreationHandle.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/ActivityRecreationHandle.kt index f072da2fa..985205ed6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/ActivityRecreationHandle.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/ActivityRecreationHandle.kt @@ -4,8 +4,11 @@ import android.app.Activity import android.app.Application.ActivityLifecycleCallbacks import android.os.Bundle import java.util.* +import javax.inject.Inject +import javax.inject.Singleton -class ActivityRecreationHandle : ActivityLifecycleCallbacks { +@Singleton +class ActivityRecreationHandle @Inject constructor() : ActivityLifecycleCallbacks { private val activities = WeakHashMap() @@ -31,4 +34,4 @@ class ActivityRecreationHandle : ActivityLifecycleCallbacks { val snapshot = activities.keys.toList() snapshot.forEach { it.recreate() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/BaseActivityEntryPoint.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/BaseActivityEntryPoint.kt new file mode 100644 index 000000000..232529d52 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/BaseActivityEntryPoint.kt @@ -0,0 +1,18 @@ +package org.koitharu.kotatsu.base.ui.util + +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.core.prefs.AppSettings + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface BaseActivityEntryPoint { + val settings: AppSettings +} + +// Hilt cannot inject into parametrized classes +fun BaseActivityEntryPoint.inject(activity: BaseActivity<*>) { + activity.settings = settings +} diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/BookmarksModule.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/BookmarksModule.kt deleted file mode 100644 index b21099509..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/BookmarksModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.koitharu.kotatsu.bookmarks - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository -import org.koitharu.kotatsu.bookmarks.ui.BookmarksViewModel - -val bookmarksModule - get() = module { - - factory { BookmarksRepository(get()) } - - viewModel { BookmarksViewModel(get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt index ff8842647..9ba79d92b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.bookmarks.domain import android.database.SQLException import androidx.room.withTransaction +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.base.domain.ReversibleHandle @@ -17,7 +18,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.ext.mapItems import org.koitharu.kotatsu.utils.ext.printStackTraceDebug -class BookmarksRepository( +class BookmarksRepository @Inject constructor( private val db: MangaDatabase, ) { @@ -86,4 +87,4 @@ class BookmarksRepository( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksActivity.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksActivity.kt index ee6c53efb..1fdd07e6c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksActivity.kt @@ -6,10 +6,12 @@ import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.fragment.app.commit +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityContainerBinding +@AndroidEntryPoint class BookmarksActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -29,7 +31,7 @@ class BookmarksActivity : BaseActivity() { with(binding.toolbar) { updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) } } @@ -38,4 +40,4 @@ class BookmarksActivity : BaseActivity() { fun newIntent(context: Context) = Intent(context, BookmarksActivity::class.java) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt index b5a55e328..e19c8d413 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt @@ -5,9 +5,11 @@ import android.view.* import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets import androidx.core.view.updatePadding +import androidx.fragment.app.viewModels +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.reverseAsync import org.koitharu.kotatsu.base.ui.BaseFragment @@ -30,13 +32,17 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf +@AndroidEntryPoint class BookmarksFragment : BaseFragment(), ListStateHolderListener, OnListItemClickListener, SectionedSelectionController.Callback { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by viewModels() private var adapter: BookmarksGroupAdapter? = null private var selectionController: SectionedSelectionController? = null @@ -53,7 +59,7 @@ class BookmarksFragment : ) adapter = BookmarksGroupAdapter( lifecycleOwner = viewLifecycleOwner, - coil = get(), + coil = coil, listener = this, selectionController = checkNotNull(selectionController), bookmarkClickListener = this, diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksViewModel.kt index 623bfc834..4c6d48a55 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksViewModel.kt @@ -2,6 +2,8 @@ package org.koitharu.kotatsu.bookmarks.ui import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map @@ -18,7 +20,8 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class BookmarksViewModel( +@HiltViewModel +class BookmarksViewModel @Inject constructor( private val repository: BookmarksRepository, ) : BaseViewModel() { @@ -33,7 +36,7 @@ class BookmarksViewModel( textPrimary = R.string.no_bookmarks_yet, textSecondary = R.string.no_bookmarks_summary, actionStringRes = 0, - ) + ), ) } else list.map { (manga, bookmarks) -> BookmarksGroup(manga, bookmarks) @@ -42,11 +45,10 @@ class BookmarksViewModel( .catch { e -> e.toErrorState(canRetry = false) } .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) - fun removeBookmarks(ids: Map>) { launchJob(Dispatchers.Default) { val handle = repository.removeBookmarks(ids) onActionDone.postCall(ReversibleAction(R.string.bookmarks_removed, handle)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index 8c1de2625..683d8abb0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -11,21 +11,27 @@ import android.webkit.WebSettings import androidx.core.view.isInvisible import androidx.fragment.app.setFragmentResult import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.android.ext.android.get +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.AlertDialogFragment +import org.koitharu.kotatsu.core.network.AndroidCookieJar import org.koitharu.kotatsu.core.network.UserAgentInterceptor import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding import org.koitharu.kotatsu.utils.ext.stringArgument import org.koitharu.kotatsu.utils.ext.withArgs +@AndroidEntryPoint class CloudFlareDialog : AlertDialogFragment(), CloudFlareCallback { private val url by stringArgument(ARG_URL) private val pendingResult = Bundle(1) + @Inject + lateinit var cookieJar: AndroidCookieJar + override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentCloudflareBinding.inflate(inflater, container, false) @SuppressLint("SetJavaScriptEnabled") @@ -38,7 +44,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud databaseEnabled = true userAgentString = UserAgentInterceptor.userAgent } - binding.webView.webViewClient = CloudFlareClient(get(), this, url.orEmpty()) + binding.webView.webViewClient = CloudFlareClient(cookieJar, this, url.orEmpty()) CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true) if (url.isNullOrEmpty()) { dismissAllowingStateLoss() @@ -90,4 +96,4 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud putString(ARG_URL, url) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt new file mode 100644 index 000000000..1b9c8109c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt @@ -0,0 +1,160 @@ +package org.koitharu.kotatsu.core + +import android.app.Application +import android.content.Context +import android.provider.SearchRecentSuggestions +import android.text.Html +import androidx.collection.arraySetOf +import androidx.room.InvalidationTracker +import coil.ComponentRegistry +import coil.ImageLoader +import coil.decode.SvgDecoder +import coil.disk.DiskCache +import coil.util.DebugLogger +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ElementsIntoSet +import java.util.concurrent.TimeUnit +import javax.inject.Singleton +import kotlinx.coroutines.Dispatchers +import okhttp3.CookieJar +import okhttp3.OkHttpClient +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.base.ui.util.ActivityRecreationHandle +import org.koitharu.kotatsu.core.db.MangaDatabase +import org.koitharu.kotatsu.core.network.* +import org.koitharu.kotatsu.core.os.ShortcutsUpdater +import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.parser.favicon.FaviconFetcher +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.local.data.CacheDir +import org.koitharu.kotatsu.local.data.CbzFetcher +import org.koitharu.kotatsu.local.data.LocalStorageManager +import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider +import org.koitharu.kotatsu.settings.backup.BackupObserver +import org.koitharu.kotatsu.sync.domain.SyncController +import org.koitharu.kotatsu.utils.ext.isLowRamDevice +import org.koitharu.kotatsu.utils.image.CoilImageGetter +import org.koitharu.kotatsu.widget.WidgetUpdater + +@Module +@InstallIn(SingletonComponent::class) +interface AppModule { + + @Binds + fun bindCookieJar(androidCookieJar: AndroidCookieJar): CookieJar + + @Binds + fun bindMangaLoaderContext(mangaLoaderContextImpl: MangaLoaderContextImpl): MangaLoaderContext + + @Binds + fun bindImageGetter(coilImageGetter: CoilImageGetter): Html.ImageGetter + + companion object { + + @Provides + @Singleton + fun provideOkHttpClient( + localStorageManager: LocalStorageManager, + cookieJar: CookieJar, + settings: AppSettings, + ): OkHttpClient { + val cache = localStorageManager.createHttpCache() + return OkHttpClient.Builder().apply { + connectTimeout(20, TimeUnit.SECONDS) + readTimeout(60, TimeUnit.SECONDS) + writeTimeout(20, TimeUnit.SECONDS) + cookieJar(cookieJar) + dns(DoHManager(cache, settings)) + cache(cache) + addInterceptor(GZipInterceptor()) + addInterceptor(UserAgentInterceptor()) + addInterceptor(CloudFlareInterceptor()) + }.build() + } + + @Provides + @Singleton + fun provideMangaDatabase( + @ApplicationContext context: Context, + ): MangaDatabase { + return MangaDatabase(context) + } + + @Provides + @Singleton + fun provideCoil( + @ApplicationContext context: Context, + okHttpClient: OkHttpClient, + mangaRepositoryFactory: MangaRepository.Factory, + ): ImageLoader { + val httpClientFactory = { + okHttpClient.newBuilder() + .cache(null) + .build() + } + val diskCacheFactory = { + val rootDir = context.externalCacheDir ?: context.cacheDir + DiskCache.Builder() + .directory(rootDir.resolve(CacheDir.THUMBS.dir)) + .build() + } + return ImageLoader.Builder(context) + .okHttpClient(httpClientFactory) + .interceptorDispatcher(Dispatchers.Default) + .fetcherDispatcher(Dispatchers.IO) + .decoderDispatcher(Dispatchers.Default) + .transformationDispatcher(Dispatchers.Default) + .diskCache(diskCacheFactory) + .logger(if (BuildConfig.DEBUG) DebugLogger() else null) + .allowRgb565(isLowRamDevice(context)) + .components( + ComponentRegistry.Builder() + .add(SvgDecoder.Factory()) + .add(CbzFetcher.Factory()) + .add(FaviconFetcher.Factory(context, okHttpClient, mangaRepositoryFactory)) + .build(), + ).build() + } + + @Provides + fun provideSearchSuggestions( + @ApplicationContext context: Context, + ): SearchRecentSuggestions { + return MangaSuggestionsProvider.createSuggestions(context) + } + + @Provides + @Singleton + @ElementsIntoSet + fun provideDatabaseObservers( + widgetUpdater: WidgetUpdater, + shortcutsUpdater: ShortcutsUpdater, + backupObserver: BackupObserver, + syncController: SyncController, + ): Set<@JvmSuppressWildcards InvalidationTracker.Observer> = arraySetOf( + widgetUpdater, + shortcutsUpdater, + backupObserver, + syncController, + ) + + @Provides + @Singleton + @ElementsIntoSet + fun provideActivityLifecycleCallbacks( + appProtectHelper: AppProtectHelper, + activityRecreationHandle: ActivityRecreationHandle, + ): Set<@JvmSuppressWildcards Application.ActivityLifecycleCallbacks> = arraySetOf( + appProtectHelper, + activityRecreationHandle, + ) + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt index 27dd10255..11e16e192 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.core.backup import androidx.room.withTransaction +import javax.inject.Inject import org.json.JSONArray import org.json.JSONObject import org.koitharu.kotatsu.BuildConfig @@ -10,7 +11,7 @@ import org.koitharu.kotatsu.parsers.util.json.mapJSON private const val PAGE_SIZE = 10 -class BackupRepository(private val db: MangaDatabase) { +class BackupRepository @Inject constructor(private val db: MangaDatabase) { suspend fun dumpHistory(): BackupEntry { var offset = 0 @@ -125,4 +126,4 @@ class BackupRepository(private val db: MangaDatabase) { } return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt deleted file mode 100644 index 150dba8dd..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.koitharu.kotatsu.core.db - -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module - -val databaseModule - get() = module { - single { MangaDatabase(androidContext()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateModule.kt deleted file mode 100644 index 9e5b0b29e..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateModule.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.koitharu.kotatsu.core.github - -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module - -val appUpdateModule - get() = module { - single { AppUpdateRepository(androidContext(), get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateRepository.kt index 619472a1d..62ea43d64 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/github/AppUpdateRepository.kt @@ -3,11 +3,14 @@ package org.koitharu.kotatsu.core.github import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageManager +import dagger.hilt.android.qualifiers.ApplicationContext import java.io.ByteArrayInputStream import java.io.InputStream import java.security.MessageDigest import java.security.cert.CertificateFactory import java.security.cert.X509Certificate +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import okhttp3.OkHttpClient @@ -22,8 +25,9 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug private const val CERT_SHA1 = "2C:19:C7:E8:07:61:2B:8E:94:51:1B:FD:72:67:07:64:5D:C2:58:AE" -class AppUpdateRepository( - private val context: Context, +@Singleton +class AppUpdateRepository @Inject constructor( + @ApplicationContext private val context: Context, private val okHttp: OkHttpClient, ) { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/AndroidCookieJar.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/AndroidCookieJar.kt index fb806bda1..bb221d743 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/AndroidCookieJar.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/AndroidCookieJar.kt @@ -1,13 +1,16 @@ package org.koitharu.kotatsu.core.network import android.webkit.CookieManager +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import okhttp3.Cookie import okhttp3.CookieJar import okhttp3.HttpUrl -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine -class AndroidCookieJar : CookieJar { +@Singleton +class AndroidCookieJar @Inject constructor() : CookieJar { private val cookieManager = CookieManager.getInstance() @@ -31,4 +34,4 @@ class AndroidCookieJar : CookieJar { suspend fun clear() = suspendCoroutine { continuation -> cookieManager.removeAllCookies(continuation::resume) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt deleted file mode 100644 index 1eb6a1021..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.koitharu.kotatsu.core.network - -import okhttp3.CookieJar -import okhttp3.OkHttpClient -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl -import org.koitharu.kotatsu.local.data.LocalStorageManager -import org.koitharu.kotatsu.parsers.MangaLoaderContext -import java.util.concurrent.TimeUnit - -val networkModule - get() = module { - single { AndroidCookieJar() } bind CookieJar::class - single { - val cache = get().createHttpCache() - OkHttpClient.Builder().apply { - connectTimeout(20, TimeUnit.SECONDS) - readTimeout(60, TimeUnit.SECONDS) - writeTimeout(20, TimeUnit.SECONDS) - cookieJar(get()) - dns(DoHManager(cache, get())) - cache(cache) - addInterceptor(GZipInterceptor()) - addInterceptor(UserAgentInterceptor()) - addInterceptor(CloudFlareInterceptor()) - }.build() - } - single { MangaLoaderContextImpl(get(), get(), get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt index a201295c5..c7eb5bbf9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt @@ -13,6 +13,9 @@ import androidx.core.graphics.drawable.IconCompat import androidx.room.InvalidationTracker import coil.ImageLoader import coil.request.ImageRequest +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -26,8 +29,9 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.processLifecycleScope import org.koitharu.kotatsu.utils.ext.requireBitmap -class ShortcutsUpdater( - private val context: Context, +@Singleton +class ShortcutsUpdater @Inject constructor( + @ApplicationContext private val context: Context, private val coil: ImageLoader, private val historyRepository: HistoryRepository, private val mangaRepository: MangaDataRepository, @@ -37,6 +41,9 @@ class ShortcutsUpdater( private var shortcutsUpdateJob: Job? = null override fun onInvalidated(tables: MutableSet) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { + return + } val prevJob = shortcutsUpdateJob shortcutsUpdateJob = processLifecycleScope.launch(Dispatchers.Default) { prevJob?.join() @@ -48,7 +55,7 @@ class ShortcutsUpdater( return ShortcutManagerCompat.requestPinShortcut( context, buildShortcutInfo(manga).build(), - null + null, ) } @@ -73,12 +80,12 @@ class ShortcutsUpdater( ImageRequest.Builder(context) .data(manga.coverUrl) .size(iconSize.width, iconSize.height) - .build() + .build(), ).requireBitmap() ThumbnailUtils.extractThumbnail(bmp, iconSize.width, iconSize.height, 0) }.fold( onSuccess = { IconCompat.createWithAdaptiveBitmap(it) }, - onFailure = { IconCompat.createWithResource(context, R.drawable.ic_shortcut_default) } + onFailure = { IconCompat.createWithResource(context, R.drawable.ic_shortcut_default) }, ) mangaRepository.storeManga(manga) return ShortcutInfoCompat.Builder(context, manga.id.toString()) @@ -87,7 +94,7 @@ class ShortcutsUpdater( .setIcon(icon) .setIntent( ReaderActivity.newIntent(context, manga.id) - .setAction(ReaderActivity.ACTION_MANGA_READ) + .setAction(ReaderActivity.ACTION_MANGA_READ), ) } @@ -102,4 +109,4 @@ class ShortcutsUpdater( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt index ffacb6c6e..1d4d9784f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt @@ -5,6 +5,12 @@ import android.content.Context import android.util.Base64 import android.webkit.WebView import androidx.core.os.LocaleListCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.OkHttpClient @@ -14,14 +20,12 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.config.MangaSourceConfig import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.toList -import java.util.* -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine -class MangaLoaderContextImpl( +@Singleton +class MangaLoaderContextImpl @Inject constructor( override val httpClient: OkHttpClient, override val cookieJar: AndroidCookieJar, - private val androidContext: Context, + @ApplicationContext private val androidContext: Context, ) : MangaLoaderContext() { @SuppressLint("SetJavaScriptEnabled") @@ -50,4 +54,4 @@ class MangaLoaderContextImpl( override fun getPreferredLocales(): List { return LocaleListCompat.getAdjustedDefault().toList() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt index 90c84d5a8..5c1c71e82 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt @@ -2,9 +2,11 @@ package org.koitharu.kotatsu.core.parser import java.lang.ref.WeakReference import java.util.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.get +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.collections.set import org.koitharu.kotatsu.local.domain.LocalMangaRepository +import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.model.* interface MangaRepository { @@ -25,21 +27,25 @@ interface MangaRepository { suspend fun getTags(): Set - companion object : KoinComponent { + @Singleton + class Factory @Inject constructor( + private val localMangaRepository: LocalMangaRepository, + private val loaderContext: MangaLoaderContext, + ) { private val cache = EnumMap>(MangaSource::class.java) - operator fun invoke(source: MangaSource): MangaRepository { + fun create(source: MangaSource): MangaRepository { if (source == MangaSource.LOCAL) { - return get() + return localMangaRepository } cache[source]?.get()?.let { return it } return synchronized(cache) { cache[source]?.get()?.let { return it } - val repository = RemoteMangaRepository(MangaParser(source, get())) + val repository = RemoteMangaRepository(MangaParser(source, loaderContext)) cache[source] = WeakReference(repository) repository } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index 8162d16ac..aaae4d913 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -14,6 +14,7 @@ import coil.network.HttpException import coil.request.Options import coil.size.Size import coil.size.pxOrElse +import java.net.HttpURLConnection import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -25,7 +26,6 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.await -import java.net.HttpURLConnection private const val FALLBACK_SIZE = 9999 // largest icon @@ -34,6 +34,7 @@ class FaviconFetcher( private val diskCache: Lazy, private val mangaSource: MangaSource, private val options: Options, + private val mangaRepositoryFactory: MangaRepository.Factory, ) : Fetcher { private val diskCacheKey @@ -44,7 +45,7 @@ class FaviconFetcher( override suspend fun fetch(): FetchResult { getCached(options)?.let { return it } - val repo = MangaRepository(mangaSource) as RemoteMangaRepository + val repo = mangaRepositoryFactory.create(mangaSource) as RemoteMangaRepository val favicons = repo.getFavicons() val sizePx = maxOf( options.size.width.pxOrElse { FALLBACK_SIZE }, @@ -136,6 +137,7 @@ class FaviconFetcher( class Factory( context: Context, private val okHttpClient: OkHttpClient, + private val mangaRepositoryFactory: MangaRepository.Factory, ) : Fetcher.Factory { private val diskCache = lazy { @@ -148,7 +150,7 @@ class FaviconFetcher( override fun create(data: Uri, options: Options, imageLoader: ImageLoader): Fetcher? { return if (data.scheme == URI_SCHEME_FAVICON) { val mangaSource = MangaSource.valueOf(data.schemeSpecificPart) - FaviconFetcher(okHttpClient, diskCache, mangaSource, options) + FaviconFetcher(okHttpClient, diskCache, mangaSource, options, mangaRepositoryFactory) } else { null } @@ -156,4 +158,4 @@ class FaviconFetcher( } class FaviconMetadata(val source: MangaSource) : ImageSource.Metadata() -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index cbb330d6e..6838c160f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -10,6 +10,13 @@ import androidx.collection.arraySetOf import androidx.core.content.edit import androidx.preference.PreferenceManager import com.google.android.material.color.DynamicColors +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.network.DoHProvider @@ -18,12 +25,9 @@ import org.koitharu.kotatsu.utils.ext.getEnumValue import org.koitharu.kotatsu.utils.ext.observe import org.koitharu.kotatsu.utils.ext.putEnumValue import org.koitharu.kotatsu.utils.ext.toUriOrNull -import java.io.File -import java.text.DateFormat -import java.text.SimpleDateFormat -import java.util.* -class AppSettings(context: Context) { +@Singleton +class AppSettings @Inject constructor(@ApplicationContext context: Context) { private val prefs = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/ui/uiModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/ui/uiModule.kt deleted file mode 100644 index 8505ab567..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/ui/uiModule.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.koitharu.kotatsu.core.ui - -import android.text.Html -import coil.ComponentRegistry -import coil.ImageLoader -import coil.decode.SvgDecoder -import coil.disk.DiskCache -import coil.util.DebugLogger -import kotlinx.coroutines.Dispatchers -import okhttp3.OkHttpClient -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module -import org.koitharu.kotatsu.BuildConfig -import org.koitharu.kotatsu.core.parser.favicon.FaviconFetcher -import org.koitharu.kotatsu.local.data.CacheDir -import org.koitharu.kotatsu.local.data.CbzFetcher -import org.koitharu.kotatsu.utils.ext.isLowRamDevice -import org.koitharu.kotatsu.utils.image.CoilImageGetter - -val uiModule - get() = module { - single { - val httpClientFactory = { - get().newBuilder() - .cache(null) - .build() - } - val diskCacheFactory = { - val context = androidContext() - val rootDir = context.externalCacheDir ?: context.cacheDir - DiskCache.Builder() - .directory(rootDir.resolve(CacheDir.THUMBS.dir)) - .build() - } - ImageLoader.Builder(androidContext()) - .okHttpClient(httpClientFactory) - .interceptorDispatcher(Dispatchers.Default) - .fetcherDispatcher(Dispatchers.IO) - .decoderDispatcher(Dispatchers.Default) - .transformationDispatcher(Dispatchers.Default) - .diskCache(diskCacheFactory) - .logger(if (BuildConfig.DEBUG) DebugLogger() else null) - .allowRgb565(isLowRamDevice(androidContext())) - .components( - ComponentRegistry.Builder() - .add(SvgDecoder.Factory()) - .add(CbzFetcher.Factory()) - .add(FaviconFetcher.Factory(androidContext(), get())) - .build() - ).build() - } - factory { CoilImageGetter(androidContext(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/details/DetailsModule.kt b/app/src/main/java/org/koitharu/kotatsu/details/DetailsModule.kt deleted file mode 100644 index 5091ee0e0..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/details/DetailsModule.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.koitharu.kotatsu.details - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.details.ui.DetailsViewModel - -val detailsModule - get() = module { - - viewModel { intent -> - DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt index 4b8328e28..87f6e8fcf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt @@ -11,8 +11,9 @@ import androidx.core.view.MenuProvider import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding +import androidx.fragment.app.activityViewModels import com.google.android.material.snackbar.Snackbar -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import kotlin.math.roundToInt import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.ListSelectionController @@ -30,7 +31,6 @@ import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback import org.koitharu.kotatsu.utils.ext.addMenuProvider import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf -import kotlin.math.roundToInt class ChaptersFragment : BaseFragment(), @@ -40,14 +40,14 @@ class ChaptersFragment : SearchView.OnQueryTextListener, ListSelectionController.Callback { - private val viewModel by sharedViewModel() + private val viewModel by activityViewModels() private var chaptersAdapter: ChaptersAdapter? = null private var selectionController: ListSelectionController? = null override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentChaptersBinding.inflate(inflater, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -98,7 +98,7 @@ class ChaptersFragment : manga = viewModel.manga.value ?: return, state = ReaderState(item.chapter.id, 0, 0), ), - scaleUpActivityOptionsOf(view).toBundle() + scaleUpActivityOptionsOf(view).toBundle(), ) } @@ -128,7 +128,7 @@ class ChaptersFragment : Snackbar.make( binding.recyclerViewChapters, R.string.chapters_will_removed_background, - Snackbar.LENGTH_LONG + Snackbar.LENGTH_LONG, ).show() } } @@ -286,4 +286,4 @@ class ChaptersFragment : else -> false } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index e821d08dc..1d146e96a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -8,7 +8,6 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View -import android.view.ViewGroup import android.widget.AdapterView import android.widget.Spinner import android.widget.Toast @@ -16,7 +15,6 @@ import androidx.appcompat.view.ActionMode import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.Insets import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.commit import androidx.lifecycle.lifecycleScope @@ -24,10 +22,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.ui.BaseActivity @@ -45,17 +42,25 @@ import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.scrobbling.ui.selector.ScrobblingSelectorBottomSheet import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.isReportable import org.koitharu.kotatsu.utils.ext.report +@AndroidEntryPoint class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrategy, AdapterView.OnItemSelectedListener { - private val viewModel by viewModel { - parametersOf(MangaIntent(intent)) + @Inject + lateinit var viewModelFactory: DetailsViewModel.Factory + + @Inject + lateinit var shortcutsUpdater: ShortcutsUpdater + + private val viewModel by assistedViewModels { + viewModelFactory.create(MangaIntent(intent)) } private val downloadReceiver = object : BroadcastReceiver() { @@ -103,8 +108,9 @@ class DetailsActivity : private fun onMangaRemoved(manga: Manga) { Toast.makeText( - this, getString(R.string._s_deleted_from_local_storage, manga.title), - Toast.LENGTH_SHORT + this, + getString(R.string._s_deleted_from_local_storage, manga.title), + Toast.LENGTH_SHORT, ).show() finishAfterTransition() } @@ -130,7 +136,7 @@ class DetailsActivity : onActionClick = { e.report("DetailsActivity::onError") dismiss() - } + }, ) } else -> { @@ -141,11 +147,11 @@ class DetailsActivity : override fun onWindowInsetsChanged(insets: Insets) { binding.snackbar.updatePadding( - bottom = insets.bottom + bottom = insets.bottom, ) binding.root.updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) } @@ -222,7 +228,7 @@ class DetailsActivity : R.id.action_shortcut -> { viewModel.manga.value?.let { lifecycleScope.launch { - if (!get().requestPinShortcut(it)) { + if (!shortcutsUpdater.requestPinShortcut(it)) { binding.snackbar.show(getString(R.string.operation_not_supported)) } } @@ -272,8 +278,8 @@ class DetailsActivity : ReaderActivity.newIntent( context = this@DetailsActivity, manga = remoteManga, - state = ReaderState(chapterId, 0, 0) - ) + state = ReaderState(chapterId, 0, 0), + ), ) } setNeutralButton(R.string.download) { _, _ -> @@ -347,8 +353,8 @@ class DetailsActivity : dialogBuilder.setMessage( getString( R.string.large_manga_save_confirm, - resources.getQuantityString(R.plurals.chapters, chaptersCount, chaptersCount) - ) + resources.getQuantityString(R.plurals.chapters, chaptersCount, chaptersCount), + ), ).setPositiveButton(R.string.save) { _, _ -> DownloadService.start(this, manga) } @@ -368,4 +374,4 @@ class DetailsActivity : .putExtra(MangaIntent.KEY_ID, mangaId) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 5599762ae..f9d17a06c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -12,13 +12,14 @@ import androidx.core.view.MenuProvider import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.view.updatePadding +import androidx.fragment.app.activityViewModels import coil.ImageLoader import coil.request.ImageRequest import coil.util.CoilUtils import com.google.android.material.chip.Chip +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -45,6 +46,7 @@ import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.ShareHelper import org.koitharu.kotatsu.utils.ext.* +@AndroidEntryPoint class DetailsFragment : BaseFragment(), View.OnClickListener, @@ -52,8 +54,10 @@ class DetailsFragment : ChipsView.OnChipClickListener, OnListItemClickListener { - private val viewModel by sharedViewModel() - private val coil by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var coil: ImageLoader + + private val viewModel by activityViewModels() override fun onInflateView( inflater: LayoutInflater, @@ -263,7 +267,7 @@ class DetailsFragment : context = context ?: return, manga = manga, branch = viewModel.selectedBranchValue, - ) + ), ) } } @@ -273,13 +277,13 @@ class DetailsFragment : context = v.context, source = manga.source, query = manga.author ?: return, - ) + ), ) } R.id.imageView_cover -> { startActivity( ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }), - scaleUpActivityOptionsOf(v).toBundle() + scaleUpActivityOptionsOf(v).toBundle(), ) } } @@ -305,8 +309,8 @@ class DetailsFragment : c.chapter.branch == branch }?.let { c -> ReaderState(c.chapter.id, 0, 0) - } - ) + }, + ), ) true } @@ -329,7 +333,7 @@ class DetailsFragment : binding.root.updatePadding( left = insets.left, right = insets.right, - bottom = insets.bottom + bottom = insets.bottom, ) } @@ -343,7 +347,7 @@ class DetailsFragment : isCheckable = false, isChecked = false, ) - } + }, ) } @@ -386,4 +390,4 @@ class DetailsFragment : else -> false } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index 6c46cc25e..ff3cbe7ea 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -6,6 +6,10 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.asFlow import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import java.io.IOException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* @@ -17,6 +21,7 @@ import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository +import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.details.domain.BranchComparator @@ -33,10 +38,9 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.printStackTraceDebug -import java.io.IOException -class DetailsViewModel( - intent: MangaIntent, +class DetailsViewModel @AssistedInject constructor( + @Assisted intent: MangaIntent, private val historyRepository: HistoryRepository, favouritesRepository: FavouritesRepository, private val localMangaRepository: LocalMangaRepository, @@ -44,16 +48,20 @@ class DetailsViewModel( mangaDataRepository: MangaDataRepository, private val bookmarksRepository: BookmarksRepository, private val settings: AppSettings, - private val scrobbler: Scrobbler, + scrobblers: Set<@JvmSuppressWildcards Scrobbler>, private val imageGetter: Html.ImageGetter, + mangaRepositoryFactory: MangaRepository.Factory, ) : BaseViewModel() { + private val scrobbler = scrobblers.first() // TODO support multiple scrobblers + private val delegate = MangaDetailsDelegate( intent = intent, settings = settings, mangaDataRepository = mangaDataRepository, historyRepository = historyRepository, localMangaRepository = localMangaRepository, + mangaRepositoryFactory = mangaRepositoryFactory, ) private var loadingJob: Job @@ -110,7 +118,7 @@ class DetailsViewModel( val selectedBranchIndex = combine( branches.asFlow(), - delegate.selectedBranch + delegate.selectedBranch, ) { branches, selected -> branches.indexOf(selected) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, -1) @@ -225,7 +233,7 @@ class DetailsViewModel( fun unregisterScrobbling() { launchJob(Dispatchers.Default) { scrobbler.unregisterScrobbling( - mangaId = delegate.mangaId + mangaId = delegate.mangaId, ) } } @@ -242,4 +250,10 @@ class DetailsViewModel( it.chapter.name.contains(query, ignoreCase = true) } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(intent: MangaIntent): DetailsViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt index 3a4eca7ca..4fd58b55c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt @@ -27,6 +27,7 @@ class MangaDetailsDelegate( private val mangaDataRepository: MangaDataRepository, private val historyRepository: HistoryRepository, private val localMangaRepository: LocalMangaRepository, + private val mangaRepositoryFactory: MangaRepository.Factory, ) { private val mangaData = MutableStateFlow(intent.manga) @@ -42,7 +43,7 @@ class MangaDetailsDelegate( suspend fun doLoad() { var manga = mangaDataRepository.resolveIntent(intent) ?: throw NotFoundException("Cannot find manga", "") mangaData.value = manga - manga = MangaRepository(manga.source).getDetails(manga) + manga = mangaRepositoryFactory.create(manga.source).getDetails(manga) // find default branch val hist = historyRepository.getOne(manga) selectedBranch.value = if (hist != null) { @@ -55,7 +56,7 @@ class MangaDetailsDelegate( relatedManga.value = runCatching { if (manga.source == MangaSource.LOCAL) { val m = localMangaRepository.getRemoteManga(manga) ?: return@runCatching null - MangaRepository(m.source).getDetails(m) + mangaRepositoryFactory.create(m.source).getDetails(m) } else { localMangaRepository.findSavedManga(manga) } @@ -181,4 +182,4 @@ class MangaDetailsDelegate( } return groups.maxByOrNull { it.value.size }?.key } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt index 691bd7ad6..b6f086271 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt @@ -13,10 +13,11 @@ import android.widget.Toast import androidx.appcompat.widget.PopupMenu import androidx.core.net.toUri import androidx.fragment.app.FragmentManager +import androidx.fragment.app.activityViewModels import coil.ImageLoader import coil.request.ImageRequest -import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.databinding.SheetScrobblingBinding @@ -30,6 +31,7 @@ import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf +@AndroidEntryPoint class ScrobblingInfoBottomSheet : BaseBottomSheet(), AdapterView.OnItemSelectedListener, @@ -37,8 +39,10 @@ class ScrobblingInfoBottomSheet : View.OnClickListener, PopupMenu.OnMenuItemClickListener { - private val viewModel by sharedViewModel() - private val coil by inject(mode = LazyThreadSafetyMode.NONE) + private val viewModel by activityViewModels() + + @Inject + lateinit var coil: ImageLoader private var menu: PopupMenu? = null override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetScrobblingBinding { @@ -131,7 +135,7 @@ class ScrobblingInfoBottomSheet : val url = viewModel.scrobblingInfo.value?.externalUrl ?: return false val intent = Intent(Intent.ACTION_VIEW, url.toUri()) startActivity( - Intent.createChooser(intent, getString(R.string.open_in_browser)) + Intent.createChooser(intent, getString(R.string.open_in_browser)), ) } R.id.action_unregister -> { @@ -146,4 +150,4 @@ class ScrobblingInfoBottomSheet : } return true } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt b/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt index bc8ab323c..dbc3bc2fc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt @@ -5,6 +5,11 @@ import android.net.ConnectivityManager import android.webkit.MimeTypeMap import coil.ImageLoader import coil.request.ImageRequest +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Semaphore @@ -26,30 +31,30 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.waitForNetwork import org.koitharu.kotatsu.utils.progress.ProgressJob -import java.io.File private const val MAX_DOWNLOAD_ATTEMPTS = 3 private const val DOWNLOAD_ERROR_DELAY = 500L private const val SLOWDOWN_DELAY = 200L -class DownloadManager( - private val coroutineScope: CoroutineScope, - private val context: Context, +class DownloadManager @AssistedInject constructor( + @Assisted private val coroutineScope: CoroutineScope, + @ApplicationContext private val context: Context, private val imageLoader: ImageLoader, private val okHttp: OkHttpClient, private val cache: PagesCache, private val localMangaRepository: LocalMangaRepository, private val settings: AppSettings, + private val mangaRepositoryFactory: MangaRepository.Factory, ) { private val connectivityManager = context.getSystemService( - Context.CONNECTIVITY_SERVICE + Context.CONNECTIVITY_SERVICE, ) as ConnectivityManager private val coverWidth = context.resources.getDimensionPixelSize( - androidx.core.R.dimen.compat_notification_large_icon_max_width + androidx.core.R.dimen.compat_notification_large_icon_max_width, ) private val coverHeight = context.resources.getDimensionPixelSize( - androidx.core.R.dimen.compat_notification_large_icon_max_height + androidx.core.R.dimen.compat_notification_large_icon_max_height, ) private val semaphore = Semaphore(settings.downloadsParallelism) @@ -59,7 +64,7 @@ class DownloadManager( startId: Int, ): ProgressJob { val stateFlow = MutableStateFlow( - DownloadState.Queued(startId = startId, manga = manga, cover = null) + DownloadState.Queued(startId = startId, manga = manga, cover = null), ) val job = downloadMangaImpl(manga, chaptersIds?.takeUnless { it.isEmpty() }, stateFlow, startId) return ProgressJob(job, stateFlow) @@ -71,7 +76,8 @@ class DownloadManager( outState: MutableStateFlow, startId: Int, ): Job = coroutineScope.launch(Dispatchers.Default + errorStateHandler(outState)) { - @Suppress("NAME_SHADOWING") var manga = manga + @Suppress("NAME_SHADOWING") + var manga = manga val chaptersIdsSet = chaptersIds?.toMutableSet() val cover = loadCover(manga) outState.value = DownloadState.Queued(startId, manga, cover) @@ -87,7 +93,7 @@ class DownloadManager( if (manga.source == MangaSource.LOCAL) { manga = localMangaRepository.getRemoteManga(manga) ?: error("Cannot obtain remote manga instance") } - val repo = MangaRepository(manga.source) + val repo = mangaRepositoryFactory.create(manga.source) outState.value = DownloadState.Preparing(startId, manga, cover) val data = if (manga.chapters.isNullOrEmpty()) repo.getDetails(manga) else manga output = CbzMangaOutput.get(destination, data) @@ -100,7 +106,7 @@ class DownloadManager( data.chapters } else { data.chapters?.filter { x -> chaptersIdsSet.remove(x.id) } - } + }, ) { "Chapters list must not be null" } check(chapters.isNotEmpty()) { "Chapters list must not be empty" } check(chaptersIdsSet.isNullOrEmpty()) { @@ -134,7 +140,9 @@ class DownloadManager( } outState.value = DownloadState.Progress( - startId, data, cover, + startId, + data, + cover, totalChapters = chapters.size, currentChapter = chapterIndex, totalPages = pages.size, @@ -203,27 +211,13 @@ class DownloadManager( .data(manga.coverUrl) .referer(manga.publicUrl) .size(coverWidth, coverHeight) - .build() + .build(), ).drawable }.getOrNull() - class Factory( - private val context: Context, - private val imageLoader: ImageLoader, - private val okHttp: OkHttpClient, - private val cache: PagesCache, - private val localMangaRepository: LocalMangaRepository, - private val settings: AppSettings, - ) { + @AssistedFactory + interface Factory { - fun create(coroutineScope: CoroutineScope) = DownloadManager( - coroutineScope = coroutineScope, - context = context, - imageLoader = imageLoader, - okHttp = okHttp, - cache = cache, - localMangaRepository = localMangaRepository, - settings = settings, - ) + fun create(coroutineScope: CoroutineScope): DownloadManager } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadsActivity.kt index a0c6c63dd..07124490d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadsActivity.kt @@ -7,23 +7,29 @@ import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.lifecycle.lifecycleScope +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import org.koin.android.ext.android.get import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityDownloadsBinding import org.koitharu.kotatsu.download.ui.service.DownloadService import org.koitharu.kotatsu.utils.bindServiceWithLifecycle +@AndroidEntryPoint class DownloadsActivity : BaseActivity() { + @Inject + lateinit var coil: ImageLoader + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityDownloadsBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) - val adapter = DownloadsAdapter(lifecycleScope, get()) + val adapter = DownloadsAdapter(lifecycleScope, coil) binding.recyclerView.setHasFixedSize(true) binding.recyclerView.adapter = adapter bindServiceWithLifecycle( @@ -42,11 +48,11 @@ class DownloadsActivity : BaseActivity() { binding.recyclerView.updatePadding( left = insets.left, right = insets.right, - bottom = insets.bottom + bottom = insets.bottom, ) binding.toolbar.updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) } @@ -54,4 +60,4 @@ class DownloadsActivity : BaseActivity() { fun newIntent(context: Context) = Intent(context, DownloadsActivity::class.java) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt index 91c742e73..c9cd1aec8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt @@ -11,32 +11,34 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import kotlin.collections.set import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import org.koin.android.ext.android.get -import org.koin.core.context.GlobalContext import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseService -import org.koitharu.kotatsu.base.ui.dialog.CheckBoxAlertDialog import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.download.domain.DownloadManager import org.koitharu.kotatsu.download.domain.DownloadState import org.koitharu.kotatsu.download.domain.WakeLockNode import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.connectivityManager import org.koitharu.kotatsu.utils.ext.throttle import org.koitharu.kotatsu.utils.progress.ProgressJob import org.koitharu.kotatsu.utils.progress.TimeLeftEstimator -import java.util.concurrent.TimeUnit +@AndroidEntryPoint class DownloadService : BaseService() { private lateinit var downloadManager: DownloadManager private lateinit var notificationSwitcher: ForegroundNotificationSwitcher + @Inject + lateinit var downloadManagerFactory: DownloadManager.Factory + private val jobs = LinkedHashMap>() private val jobCount = MutableStateFlow(0) private val controlReceiver = ControlReceiver() @@ -48,7 +50,7 @@ class DownloadService : BaseService() { notificationSwitcher = ForegroundNotificationSwitcher(this) val wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager) .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading") - downloadManager = get().create( + downloadManager = downloadManagerFactory.create( coroutineScope = lifecycleScope + WakeLockNode(wakeLock, TimeUnit.HOURS.toMillis(1)), ) DownloadNotification.createChannel(this) @@ -122,7 +124,7 @@ class DownloadService : BaseService() { (job.progressValue as? DownloadState.Done)?.let { sendBroadcast( Intent(ACTION_DOWNLOAD_COMPLETE) - .putExtra(EXTRA_MANGA, ParcelableManga(it.localManga, withChapters = false)) + .putExtra(EXTRA_MANGA, ParcelableManga(it.localManga, withChapters = false)), ) } notificationSwitcher.detach( @@ -131,7 +133,7 @@ class DownloadService : BaseService() { null } else { notification.create(job.progressValue, -1L) - } + }, ) stopSelf(startId) } @@ -182,27 +184,23 @@ class DownloadService : BaseService() { if (chaptersIds?.isEmpty() == true) { return } - confirmDataTransfer(context) { - val intent = Intent(context, DownloadService::class.java) - intent.putExtra(EXTRA_MANGA, ParcelableManga(manga, withChapters = false)) - if (chaptersIds != null) { - intent.putExtra(EXTRA_CHAPTERS_IDS, chaptersIds.toLongArray()) - } - ContextCompat.startForegroundService(context, intent) - Toast.makeText(context, R.string.manga_downloading_, Toast.LENGTH_SHORT).show() + val intent = Intent(context, DownloadService::class.java) + intent.putExtra(EXTRA_MANGA, ParcelableManga(manga, withChapters = false)) + if (chaptersIds != null) { + intent.putExtra(EXTRA_CHAPTERS_IDS, chaptersIds.toLongArray()) } + ContextCompat.startForegroundService(context, intent) + Toast.makeText(context, R.string.manga_downloading_, Toast.LENGTH_SHORT).show() } fun start(context: Context, manga: Collection) { if (manga.isEmpty()) { return } - confirmDataTransfer(context) { - for (item in manga) { - val intent = Intent(context, DownloadService::class.java) - intent.putExtra(EXTRA_MANGA, ParcelableManga(item, withChapters = false)) - ContextCompat.startForegroundService(context, intent) - } + for (item in manga) { + val intent = Intent(context, DownloadService::class.java) + intent.putExtra(EXTRA_MANGA, ParcelableManga(item, withChapters = false)) + ContextCompat.startForegroundService(context, intent) } } @@ -225,24 +223,5 @@ class DownloadService : BaseService() { } return null } - - private fun confirmDataTransfer(context: Context, callback: () -> Unit) { - val settings = GlobalContext.get().get() - if (context.connectivityManager.isActiveNetworkMetered && settings.isTrafficWarningEnabled) { - CheckBoxAlertDialog.Builder(context) - .setTitle(R.string.warning) - .setMessage(R.string.network_consumption_warning) - .setCheckBoxText(R.string.dont_ask_again) - .setCheckBoxChecked(false) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string._continue) { _, doNotAsk -> - settings.isTrafficWarningEnabled = !doNotAsk - callback() - }.create() - .show() - } else { - callback() - } - } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt deleted file mode 100644 index 32d55740b..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.koitharu.kotatsu.explore - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.explore.domain.ExploreRepository -import org.koitharu.kotatsu.explore.ui.ExploreViewModel - -val exploreModule - get() = module { - - factory { ExploreRepository(get(), get()) } - - viewModel { ExploreViewModel(get(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt b/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt index 730f38a29..f8bc28e98 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt @@ -1,14 +1,16 @@ package org.koitharu.kotatsu.explore.domain +import javax.inject.Inject import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.SortOrder -class ExploreRepository( +class ExploreRepository @Inject constructor( private val settings: AppSettings, private val historyRepository: HistoryRepository, + private val mangaRepositoryFactory: MangaRepository.Factory, ) { suspend fun findRandomManga(tagsLimit: Int): Manga { @@ -20,7 +22,7 @@ class ExploreRepository( val source = checkNotNull(tag?.source ?: settings.getMangaSources(includeHidden = false).randomOrNull()) { "No sources found" } - val repo = MangaRepository(source) + val repo = mangaRepositoryFactory.create(source) val list = repo.getList( offset = 0, sortOrder = if (SortOrder.UPDATED in repo.sortOrders) SortOrder.UPDATED else null, @@ -37,4 +39,4 @@ class ExploreRepository( } return list.random() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index ad399e426..b3dfd5716 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -6,10 +6,12 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -30,12 +32,17 @@ import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class ExploreFragment : BaseFragment(), +@AndroidEntryPoint +class ExploreFragment : + BaseFragment(), RecyclerViewOwner, ExploreListEventListener, OnListItemClickListener { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by viewModels() private var exploreAdapter: ExploreAdapter? = null private var paddingHorizontal = 0 @@ -48,7 +55,7 @@ class ExploreFragment : BaseFragment(), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, this, this) + exploreAdapter = ExploreAdapter(coil, viewLifecycleOwner, this, this) with(binding.recyclerView) { adapter = exploreAdapter setHasFixedSize(true) @@ -112,7 +119,7 @@ class ExploreFragment : BaseFragment(), val snackbar = Snackbar.make( binding.recyclerView, e.getDisplayMessage(resources), - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ) snackbar.anchorView = (activity as? BottomNavOwner)?.bottomNav snackbar.show() diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt index b00c63755..122c14b21 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt @@ -3,6 +3,8 @@ package org.koitharu.kotatsu.explore.ui import androidx.lifecycle.LiveData import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.R @@ -15,7 +17,8 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class ExploreViewModel( +@HiltViewModel +class ExploreViewModel @Inject constructor( private val settings: AppSettings, private val exploreRepository: ExploreRepository, ) : BaseViewModel() { @@ -66,4 +69,4 @@ class ExploreViewModel( } return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/FavouritesModule.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/FavouritesModule.kt deleted file mode 100644 index f3bc159a8..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/FavouritesModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.koitharu.kotatsu.favourites - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.favourites.domain.FavouritesRepository -import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel -import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditViewModel -import org.koitharu.kotatsu.favourites.ui.categories.select.MangaCategoriesViewModel -import org.koitharu.kotatsu.favourites.ui.list.FavouritesListViewModel - -val favouritesModule - get() = module { - - single { FavouritesRepository(get(), get()) } - - viewModel { categoryId -> - FavouritesListViewModel(categoryId.get(), get(), get(), get(), get()) - } - viewModel { FavouritesCategoriesViewModel(get(), get()) } - viewModel { manga -> - MangaCategoriesViewModel(manga.get(), get()) - } - viewModel { params -> FavouritesCategoryEditViewModel(params[0], get(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt index def0b272f..4cdbf8311 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.favourites.domain import androidx.room.withTransaction +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.base.domain.ReversibleHandle import org.koitharu.kotatsu.core.db.MangaDatabase @@ -14,7 +16,8 @@ import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels import org.koitharu.kotatsu.utils.ext.mapItems -class FavouritesRepository( +@Singleton +class FavouritesRepository @Inject constructor( private val db: MangaDatabase, private val channels: TrackerNotificationChannels, ) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt index c33b2dc6a..396304cd9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt @@ -6,6 +6,7 @@ import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.fragment.app.commit +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.model.FavouriteCategory @@ -13,6 +14,7 @@ import org.koitharu.kotatsu.databinding.ActivityContainerBinding import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID +@AndroidEntryPoint class FavouritesActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -50,4 +52,4 @@ class FavouritesActivity : BaseActivity() { .putExtra(EXTRA_CATEGORY_ID, category.id) .putExtra(EXTRA_TITLE, category.title) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index 997edc488..8d82dd03a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt @@ -9,15 +9,17 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.list.ListSelectionController @@ -33,13 +35,17 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.measureHeight import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf +@AndroidEntryPoint class FavouriteCategoriesActivity : BaseActivity(), FavouriteCategoriesListListener, View.OnClickListener, ListStateHolderListener { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by viewModels() private lateinit var adapter: CategoriesAdapter private lateinit var selectionController: ListSelectionController @@ -49,7 +55,7 @@ class FavouriteCategoriesActivity : super.onCreate(savedInstanceState) setContentView(ActivityCategoriesBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) - adapter = CategoriesAdapter(get(), this, this, this) + adapter = CategoriesAdapter(coil, this, this, this) selectionController = ListSelectionController( activity = this, decoration = CategoriesSelectionDecoration(this), @@ -169,7 +175,8 @@ class FavouriteCategoriesActivity : } private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( - ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0 + ItemTouchHelper.DOWN or ItemTouchHelper.UP, + 0, ) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index 0b2268c20..e2d7b277d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -2,6 +2,9 @@ package org.koitharu.kotatsu.favourites.ui.categories import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.* +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -16,9 +19,9 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.mapItems import org.koitharu.kotatsu.utils.ext.requireValue -import java.util.* -class FavouritesCategoriesViewModel( +@HiltViewModel +class FavouritesCategoriesViewModel @Inject constructor( private val repository: FavouritesRepository, private val settings: AppSettings, ) : BaseViewModel() { @@ -56,7 +59,7 @@ class FavouritesCategoriesViewModel( textPrimary = R.string.text_empty_holder_primary, textSecondary = R.string.empty_favourite_categories, actionStringRes = 0, - ) + ), ) } }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt index 24cb1f533..330e72f18 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt @@ -14,8 +14,8 @@ import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.model.FavouriteCategory @@ -23,13 +23,21 @@ import org.koitharu.kotatsu.core.ui.titleRes import org.koitharu.kotatsu.databinding.ActivityCategoryEditBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class FavouritesCategoryEditActivity : BaseActivity(), AdapterView.OnItemClickListener, - View.OnClickListener, TextWatcher { +@AndroidEntryPoint +class FavouritesCategoryEditActivity : + BaseActivity(), + AdapterView.OnItemClickListener, + View.OnClickListener, + TextWatcher { - private val viewModel by viewModel { - parametersOf(intent.getLongExtra(EXTRA_ID, NO_ID)) + @Inject + lateinit var viewModelFactory: FavouritesCategoryEditViewModel.Factory + + private val viewModel by assistedViewModels { + viewModelFactory.create(intent.getLongExtra(EXTRA_ID, NO_ID)) } private var selectedSortOrder: SortOrder? = null @@ -164,4 +172,4 @@ class FavouritesCategoryEditActivity : BaseActivity .putExtra(EXTRA_ID, id) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt index 9f67964bd..108966280 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt @@ -3,6 +3,9 @@ package org.koitharu.kotatsu.favourites.ui.categories.edit import androidx.lifecycle.MutableLiveData import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.FavouriteCategory @@ -13,8 +16,8 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent private const val NO_ID = -1L -class FavouritesCategoryEditViewModel( - private val categoryId: Long, +class FavouritesCategoryEditViewModel @AssistedInject constructor( + @Assisted private val categoryId: Long, private val repository: FavouritesRepository, private val settings: AppSettings, ) : BaseViewModel() { @@ -51,4 +54,10 @@ class FavouritesCategoryEditViewModel( onSaved.call(Unit) } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(categoryId: Long): FavouritesCategoryEditViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt index d55d66cea..631169e69 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesBottomSheet.kt @@ -8,8 +8,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -19,6 +18,7 @@ import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEdit import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.withArgs @@ -28,8 +28,13 @@ class FavouriteCategoriesBottomSheet : View.OnClickListener, Toolbar.OnMenuItemClickListener { - private val viewModel by viewModel { - parametersOf(requireNotNull(arguments?.getParcelableArrayList(KEY_MANGA_LIST)).map { it.manga }) + @Inject + lateinit var viewModelFactory: MangaCategoriesViewModel.Factory + + private val viewModel by assistedViewModels { + viewModelFactory.create( + requireNotNull(arguments?.getParcelableArrayList(KEY_MANGA_LIST)).map { it.manga }, + ) } private var adapter: MangaCategoriesAdapter? = null diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt index 0984c47d8..4172fa79a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt @@ -1,6 +1,9 @@ package org.koitharu.kotatsu.favourites.ui.categories.select import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -10,9 +13,9 @@ import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryI import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class MangaCategoriesViewModel( - private val manga: List, - private val favouritesRepository: FavouritesRepository +class MangaCategoriesViewModel @AssistedInject constructor( + @Assisted private val manga: List, + private val favouritesRepository: FavouritesRepository, ) : BaseViewModel() { val content = combine( @@ -23,7 +26,7 @@ class MangaCategoriesViewModel( MangaCategoryItem( id = it.id, name = it.title, - isChecked = it.id in checked + isChecked = it.id in checked, ) } }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) @@ -43,7 +46,7 @@ class MangaCategoriesViewModel( favouritesRepository.observeCategoriesIds(manga[0].id) } else { combine( - manga.map { favouritesRepository.observeCategoriesIds(it.id) } + manga.map { favouritesRepository.observeCategoriesIds(it.id) }, ) { array -> val result = HashSet() var isFirst = true @@ -58,4 +61,10 @@ class MangaCategoriesViewModel( result } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(manga: List): MangaCategoriesViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt index 343c873fc..456874d52 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt @@ -6,20 +6,25 @@ import android.view.MenuItem import android.view.View import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.PopupMenu -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.titleRes import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.withArgs +@AndroidEntryPoint class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickListener { - override val viewModel by viewModel { - parametersOf(categoryId) + @Inject + lateinit var viewModelFactory: FavouritesListViewModel.Factory + + override val viewModel by assistedViewModels { + viewModelFactory.create(categoryId) } private val categoryId: Long diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index adbd00678..cca1dffe6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -3,6 +3,9 @@ package org.koitharu.kotatsu.favourites.ui.list import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -25,8 +28,8 @@ import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class FavouritesListViewModel( - private val categoryId: Long, +class FavouritesListViewModel @AssistedInject constructor( + @Assisted private val categoryId: Long, private val repository: FavouritesRepository, private val trackingRepository: TrackingRepository, private val historyRepository: HistoryRepository, @@ -121,4 +124,10 @@ class FavouritesListViewModel( PROGRESS_NONE } } + + @AssistedFactory + interface Factory { + + fun create(categoryId: Long): FavouritesListViewModel + } } diff --git a/app/src/main/java/org/koitharu/kotatsu/history/HistoryModule.kt b/app/src/main/java/org/koitharu/kotatsu/history/HistoryModule.kt deleted file mode 100644 index 029d024f0..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/history/HistoryModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.koitharu.kotatsu.history - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.history.domain.HistoryRepository -import org.koitharu.kotatsu.history.ui.HistoryListViewModel - -val historyModule - get() = module { - - single { HistoryRepository(get(), get(), get(), getAll()) } - - viewModel { HistoryListViewModel(get(), get(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt b/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt index 7b226e8bc..68cb5b484 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.history.domain import androidx.room.withTransaction +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -20,11 +21,11 @@ import org.koitharu.kotatsu.utils.ext.mapItems const val PROGRESS_NONE = -1f -class HistoryRepository( +class HistoryRepository @Inject constructor( private val db: MangaDatabase, private val trackingRepository: TrackingRepository, private val settings: AppSettings, - private val scrobblers: List, + private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>, ) { suspend fun getList(offset: Int, limit: Int = 20): List { @@ -82,7 +83,7 @@ class HistoryRepository( scroll = scroll.toFloat(), // we migrate to int, but decide to not update database percent = percent, deletedAt = 0L, - ) + ), ) trackingRepository.syncWithHistory(manga, chapterId) val chapter = manga.chapters?.find { x -> x.id == chapterId } diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryActivity.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryActivity.kt index 4313bf957..880aaeec2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryActivity.kt @@ -9,11 +9,13 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.commit import com.google.android.material.appbar.AppBarLayout +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityContainerBinding import org.koitharu.kotatsu.main.ui.AppBarOwner +@AndroidEntryPoint class HistoryActivity : BaseActivity(), AppBarOwner { diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index bb7b984aa..0ad1f5e92 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -5,17 +5,18 @@ import android.view.Menu import android.view.MenuItem import android.view.View import androidx.appcompat.view.ActionMode -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.addMenuProvider +@AndroidEntryPoint class HistoryListFragment : MangaListFragment() { - override val viewModel by viewModel() + override val viewModel by viewModels() override val isSwipeRefreshEnabled = false override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -51,7 +52,7 @@ class HistoryListFragment : MangaListFragment() { } } - override fun onCreateAdapter() = HistoryListAdapter(get(), viewLifecycleOwner, this) + override fun onCreateAdapter() = HistoryListAdapter(coil, viewLifecycleOwner, this) companion object { diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index c2c03311f..cbf38db4b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -2,6 +2,10 @@ package org.koitharu.kotatsu.history.ui import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.* +import java.util.concurrent.TimeUnit +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -22,10 +26,9 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.daysDiff import org.koitharu.kotatsu.utils.ext.onFirst -import java.util.* -import java.util.concurrent.TimeUnit -class HistoryListViewModel( +@HiltViewModel +class HistoryListViewModel @Inject constructor( private val repository: HistoryRepository, private val settings: AppSettings, private val trackingRepository: TrackingRepository, @@ -39,7 +42,7 @@ class HistoryListViewModel( override val content = combine( repository.observeAllWithHistory(), historyGrouping, - createListModeFlow() + createListModeFlow(), ) { list, grouped, mode -> when { list.isEmpty() -> listOf( @@ -48,7 +51,7 @@ class HistoryListViewModel( textPrimary = R.string.text_history_holder_primary, textSecondary = R.string.text_history_holder_secondary, actionStringRes = 0, - ) + ), ) else -> mapList(list, grouped, mode) } @@ -87,7 +90,7 @@ class HistoryListViewModel( private suspend fun mapList( list: List, grouped: Boolean, - mode: ListMode + mode: ListMode, ): List { val result = ArrayList(if (grouped) (list.size * 1.4).toInt() else list.size + 1) val showPercent = settings.isReadingIndicatorsEnabled diff --git a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt index 28f58887b..f8761040a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -16,15 +16,18 @@ import coil.request.ImageRequest import coil.target.ViewTarget import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView -import org.koin.android.ext.android.inject +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityImageBinding import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.indicator +@AndroidEntryPoint class ImageActivity : BaseActivity() { - private val coil: ImageLoader by inject() + @Inject + lateinit var coil: ImageLoader override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,7 +43,7 @@ class ImageActivity : BaseActivity() { with(binding.toolbar) { updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) updateLayoutParams { topMargin = insets.top @@ -90,4 +93,4 @@ class ImageActivity : BaseActivity() { .setData(Uri.parse(url)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt b/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt deleted file mode 100644 index 514abcfea..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.koitharu.kotatsu.library - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.library.domain.LibraryRepository -import org.koitharu.kotatsu.library.ui.LibraryViewModel -import org.koitharu.kotatsu.library.ui.config.categories.LibraryCategoriesConfigViewModel - -val libraryModule - get() = module { - - factory { LibraryRepository(get()) } - - viewModel { LibraryViewModel(get(), get(), get(), get(), get()) } - viewModel { LibraryCategoriesConfigViewModel(get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/library/domain/LibraryRepository.kt b/app/src/main/java/org/koitharu/kotatsu/library/domain/LibraryRepository.kt index f2167cbc6..a2457fb30 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/domain/LibraryRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/domain/LibraryRepository.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.library.domain +import javax.inject.Inject import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.toManga @@ -9,7 +10,7 @@ import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity import org.koitharu.kotatsu.favourites.data.toFavouriteCategory import org.koitharu.kotatsu.parsers.model.Manga -class LibraryRepository( +class LibraryRepository @Inject constructor( private val db: MangaDatabase, ) { diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt index 726d7fb4a..a898076fb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt @@ -6,14 +6,17 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding +import androidx.fragment.app.viewModels +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.reverseAsync import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController import org.koitharu.kotatsu.base.ui.util.ReversibleAction +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentLibraryBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.favourites.ui.FavouritesActivity @@ -28,11 +31,18 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.ext.addMenuProvider import org.koitharu.kotatsu.utils.ext.getDisplayMessage +@AndroidEntryPoint class LibraryFragment : BaseFragment(), LibraryListEventListener { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + @Inject + lateinit var settings: AppSettings + + private val viewModel by viewModels() private var adapter: LibraryAdapter? = null private var selectionController: SectionedSelectionController? = null @@ -42,7 +52,7 @@ class LibraryFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val sizeResolver = ItemSizeResolver(resources, get()) + val sizeResolver = ItemSizeResolver(resources, settings) selectionController = SectionedSelectionController( activity = requireActivity(), owner = this, @@ -50,7 +60,7 @@ class LibraryFragment : ) adapter = LibraryAdapter( lifecycleOwner = viewLifecycleOwner, - coil = get(), + coil = coil, listener = this, sizeResolver = sizeResolver, selectionController = checkNotNull(selectionController), diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt index b31818c37..0d7cafb0d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt @@ -3,7 +3,9 @@ package org.koitharu.kotatsu.library.ui import androidx.collection.ArraySet import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel import java.util.* +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -30,7 +32,8 @@ import org.koitharu.kotatsu.utils.ext.daysDiff private const val HISTORY_MAX_SEGMENTS = 2 -class LibraryViewModel( +@HiltViewModel +class LibraryViewModel @Inject constructor( repository: LibraryRepository, private val historyRepository: HistoryRepository, private val favouritesRepository: FavouritesRepository, diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigSheet.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigSheet.kt index 4352f07a2..0808de746 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigSheet.kt @@ -6,19 +6,21 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager -import org.koin.androidx.viewmodel.ext.android.viewModel +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.databinding.SheetBaseBinding +@AndroidEntryPoint class LibraryCategoriesConfigSheet : BaseBottomSheet(), OnListItemClickListener, View.OnClickListener { - private val viewModel by viewModel() + private val viewModel by viewModels() override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetBaseBinding { return SheetBaseBinding.inflate(inflater, container, false) diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigViewModel.kt index 10259ed90..49be1d853 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/categories/LibraryCategoriesConfigViewModel.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.library.ui.config.categories import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -8,7 +10,8 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class LibraryCategoriesConfigViewModel( +@HiltViewModel +class LibraryCategoriesConfigViewModel @Inject constructor( private val favouritesRepository: FavouritesRepository, ) : BaseViewModel() { diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/size/LibrarySizeBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/size/LibrarySizeBottomSheet.kt index a761181f7..cb8199a56 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/config/size/LibrarySizeBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/config/size/LibrarySizeBottomSheet.kt @@ -7,7 +7,8 @@ import android.view.ViewGroup import androidx.fragment.app.FragmentManager import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider -import org.koin.android.ext.android.inject +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.core.prefs.AppSettings @@ -15,12 +16,14 @@ import org.koitharu.kotatsu.databinding.SheetLibrarySizeBinding import org.koitharu.kotatsu.utils.ext.setValueRounded import org.koitharu.kotatsu.utils.progress.IntPercentLabelFormatter +@AndroidEntryPoint class LibrarySizeBottomSheet : BaseBottomSheet(), Slider.OnChangeListener, View.OnClickListener { - private val settings by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var settings: AppSettings private var labelFormatter: LabelFormatter? = null override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetLibrarySizeBinding { diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/ListModeSelectDialog.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/ListModeSelectDialog.kt index dfbefb02d..1c988732a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/ListModeSelectDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/ListModeSelectDialog.kt @@ -8,7 +8,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider -import org.koin.android.ext.android.inject +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup @@ -17,13 +17,16 @@ import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.databinding.DialogListModeBinding import org.koitharu.kotatsu.utils.ext.setValueRounded import org.koitharu.kotatsu.utils.progress.IntPercentLabelFormatter +import javax.inject.Inject +@AndroidEntryPoint class ListModeSelectDialog : AlertDialogFragment(), CheckableButtonGroup.OnCheckedChangeListener, Slider.OnChangeListener { - private val settings by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var settings: AppSettings override fun onInflateView( inflater: LayoutInflater, diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 1cd0816df..a695176c7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -12,9 +12,10 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.recyclerview.widget.GridLayoutManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.get import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.reverseAsync import org.koitharu.kotatsu.base.ui.BaseFragment @@ -56,6 +57,9 @@ abstract class MangaListFragment : ListSelectionController.Callback2, FastScroller.FastScrollListener { + @Inject + lateinit var coil: ImageLoader + private var listAdapter: MangaListAdapter? = null private var paginationListener: PaginationScrollListener? = null private var selectionController: ListSelectionController? = null @@ -188,7 +192,7 @@ abstract class MangaListFragment : protected open fun onCreateAdapter(): MangaListAdapter { return MangaListAdapter( - coil = get(), + coil = coil, lifecycleOwner = viewLifecycleOwner, listener = this, ) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt index 0cf07b393..c80f7358a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt @@ -6,11 +6,12 @@ import android.os.Bundle import android.view.* import androidx.appcompat.widget.SearchView import androidx.fragment.app.FragmentManager -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.databinding.SheetFilterBinding import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel +import org.koitharu.kotatsu.utils.ext.parentFragmentViewModels class FilterBottomSheet : BaseBottomSheet(), @@ -18,9 +19,7 @@ class FilterBottomSheet : SearchView.OnQueryTextListener, DialogInterface.OnKeyListener { - private val viewModel by sharedViewModel( - owner = { requireParentFragment() }, - ) + private val viewModel by parentFragmentViewModels() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return super.onCreateDialog(savedInstanceState).also { diff --git a/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt b/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt index b116fdf97..e6d74c9fc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt @@ -1,20 +1 @@ package org.koitharu.kotatsu.local - -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.download.domain.DownloadManager -import org.koitharu.kotatsu.local.data.LocalStorageManager -import org.koitharu.kotatsu.local.domain.LocalMangaRepository -import org.koitharu.kotatsu.local.ui.LocalListViewModel - -val localModule - get() = module { - - factory { LocalStorageManager(androidContext(), get()) } - single { LocalMangaRepository(get()) } - - factory { DownloadManager.Factory(androidContext(), get(), get(), get(), get(), get()) } - - viewModel { LocalListViewModel(get(), get(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/local/data/LocalStorageManager.kt b/app/src/main/java/org/koitharu/kotatsu/local/data/LocalStorageManager.kt index 9c1407a32..81493561b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/data/LocalStorageManager.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/data/LocalStorageManager.kt @@ -4,6 +4,10 @@ import android.content.ContentResolver import android.content.Context import android.os.StatFs import androidx.annotation.WorkerThread +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.withContext @@ -12,15 +16,15 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.utils.ext.computeSize import org.koitharu.kotatsu.utils.ext.getStorageName -import java.io.File private const val DIR_NAME = "manga" private const val CACHE_DISK_PERCENTAGE = 0.02 private const val CACHE_SIZE_MIN: Long = 10 * 1024 * 1024 // 10MB private const val CACHE_SIZE_MAX: Long = 250 * 1024 * 1024 // 250MB -class LocalStorageManager( - private val context: Context, +@Singleton +class LocalStorageManager @Inject constructor( + @ApplicationContext private val context: Context, private val settings: AppSettings, ) { @@ -131,4 +135,4 @@ class LocalStorageManager( private fun File.isWriteable() = runCatching { canWrite() }.getOrDefault(false) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt b/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt index 10eeea740..82bbead60 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt @@ -2,15 +2,19 @@ package org.koitharu.kotatsu.local.data import android.content.Context import com.tomclaw.cache.DiskLruCache +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import java.io.InputStream +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.flow.MutableStateFlow import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.ext.longHashCode import org.koitharu.kotatsu.utils.ext.subdir import org.koitharu.kotatsu.utils.ext.takeIfReadable -import java.io.File -import java.io.InputStream -class PagesCache(context: Context) { +@Singleton +class PagesCache @Inject constructor(@ApplicationContext context: Context) { private val cacheDir = context.externalCacheDir ?: context.cacheDir private val lruCache = createDiskLruCacheSafe( @@ -70,4 +74,4 @@ private fun createDiskLruCacheSafe(dir: File, size: Long): DiskLruCache { dir.mkdir() DiskLruCache.create(dir, size) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt index 0fb9b63d3..b37b1a5b8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt @@ -12,6 +12,8 @@ import java.io.IOException import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipFile +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.* import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException @@ -31,7 +33,8 @@ import org.koitharu.kotatsu.utils.ext.resolveName private const val MAX_PARALLELISM = 4 -class LocalMangaRepository(private val storageManager: LocalStorageManager) : MangaRepository { +@Singleton +class LocalMangaRepository @Inject constructor(private val storageManager: LocalStorageManager) : MangaRepository { override val source = MangaSource.LOCAL private val filenameFilter = CbzFilter() @@ -86,7 +89,7 @@ class LocalMangaRepository(private val storageManager: LocalStorageManager) : Ma entries.filter { x -> !x.isDirectory && x.name.substringBeforeLast( File.separatorChar, - "" + "", ) == parent } } @@ -138,11 +141,11 @@ class LocalMangaRepository(private val storageManager: LocalStorageManager) : Ma url = fileUri, coverUrl = zipUri( file, - entryName = index.getCoverEntry() ?: findFirstImageEntry(zip.entries())?.name.orEmpty() + entryName = index.getCoverEntry() ?: findFirstImageEntry(zip.entries())?.name.orEmpty(), ), chapters = info.chapters?.map { c -> c.copy(url = fileUri, source = MangaSource.LOCAL) - } + }, ) } // fallback @@ -211,7 +214,7 @@ class LocalMangaRepository(private val storageManager: LocalStorageManager) : Ma return@runInterruptible info.copy2( source = MangaSource.LOCAL, url = fileUri, - chapters = info.chapters?.map { c -> c.copy(url = fileUri) } + chapters = info.chapters?.map { c -> c.copy(url = fileUri) }, ) } } @@ -342,4 +345,4 @@ class LocalMangaRepository(private val storageManager: LocalStorageManager) : Ma branch = branch, source = source, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalChaptersRemoveService.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalChaptersRemoveService.kt index 3bc53726c..a95b7bebc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalChaptersRemoveService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalChaptersRemoveService.kt @@ -8,7 +8,8 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat -import org.koin.android.ext.android.inject +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.CoroutineIntentService import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga @@ -16,9 +17,11 @@ import org.koitharu.kotatsu.download.ui.service.DownloadService import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.parsers.model.Manga +@AndroidEntryPoint class LocalChaptersRemoveService : CoroutineIntentService() { - private val localMangaRepository by inject() + @Inject + lateinit var localMangaRepository: LocalMangaRepository override suspend fun processIntent(intent: Intent?) { val manga = intent?.getParcelableExtra(EXTRA_MANGA)?.manga ?: return @@ -28,7 +31,7 @@ class LocalChaptersRemoveService : CoroutineIntentService() { localMangaRepository.deleteChapters(mangaWithChapters, chaptersIds) sendBroadcast( Intent(DownloadService.ACTION_DOWNLOAD_COMPLETE) - .putExtra(EXTRA_MANGA, ParcelableManga(manga, withChapters = false)) + .putExtra(EXTRA_MANGA, ParcelableManga(manga, withChapters = false)), ) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) } @@ -77,4 +80,4 @@ class LocalChaptersRemoveService : CoroutineIntentService() { ContextCompat.startForegroundService(context, intent) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListFragment.kt index 351f8d58d..aadfe7ad3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListFragment.kt @@ -11,9 +11,9 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.view.ActionMode import androidx.core.net.toFile import androidx.core.net.toUri +import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.download.ui.service.DownloadService @@ -25,7 +25,7 @@ import org.koitharu.kotatsu.utils.progress.Progress class LocalListFragment : MangaListFragment(), ActivityResultCallback> { - override val viewModel by viewModel() + override val viewModel by viewModels() private val importCall = registerForActivityResult( ActivityResultContracts.OpenMultipleDocuments(), this, diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt index 4498a06e2..399eb7747 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt @@ -3,6 +3,9 @@ package org.koitharu.kotatsu.local.ui import android.net.Uri import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -25,9 +28,9 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.progress.Progress -import java.io.IOException -class LocalListViewModel( +@HiltViewModel +class LocalListViewModel @Inject constructor( private val repository: LocalMangaRepository, private val historyRepository: HistoryRepository, settings: AppSettings, @@ -42,7 +45,7 @@ class LocalListViewModel( override val content = combine( mangaList, createListModeFlow(), - listError + listError, ) { list, mode, error -> when { error != null -> listOf(error.toErrorState(canRetry = true)) @@ -53,7 +56,7 @@ class LocalListViewModel( textPrimary = R.string.text_local_holder_primary, textSecondary = R.string.text_local_holder_secondary, actionStringRes = R.string._import, - ) + ), ) else -> list.toUi(mode) } @@ -125,4 +128,4 @@ class LocalListViewModel( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt b/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt deleted file mode 100644 index 64d43c526..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.koitharu.kotatsu.main - -import android.app.Application -import android.os.Build -import androidx.room.InvalidationTracker -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koitharu.kotatsu.base.ui.util.ActivityRecreationHandle -import org.koitharu.kotatsu.core.os.ShortcutsUpdater -import org.koitharu.kotatsu.main.ui.MainViewModel -import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper -import org.koitharu.kotatsu.main.ui.protect.ProtectViewModel - -val mainModule - get() = module { - single { AppProtectHelper(get()) } bind Application.ActivityLifecycleCallbacks::class - single { ActivityRecreationHandle() } bind Application.ActivityLifecycleCallbacks::class - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - single { ShortcutsUpdater(androidContext(), get(), get(), get()) } bind InvalidationTracker.Observer::class - } else { - factory { ShortcutsUpdater(androidContext(), get(), get(), get()) } - } - - viewModel { MainViewModel(get(), get(), get(), get()) } - viewModel { ProtectViewModel(get(), get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/ExitCallback.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/ExitCallback.kt index ffbafc22b..e781577b4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/ExitCallback.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/ExitCallback.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.main.ui import android.view.View -import androidx.activity.ComponentActivity import androidx.activity.OnBackPressedCallback import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar @@ -12,13 +11,13 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.koin.android.ext.android.get import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.observeAsFlow class ExitCallback( - private val activity: ComponentActivity, + private val activity: BaseActivity<*>, private val snackbarHost: View, ) : OnBackPressedCallback(false) { @@ -46,7 +45,7 @@ class ExitCallback( } private fun observeSettings() { - activity.get() + activity.settings .observeAsFlow(AppSettings.KEY_EXIT_CONFIRM) { isExitConfirmationEnabled } .flowOn(Dispatchers.Default) .onEach { isEnabled = it } diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt index 540c4a0b1..cda6a9dc7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -6,6 +6,7 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup.MarginLayoutParams import androidx.activity.result.ActivityResultCallback +import androidx.activity.viewModels import androidx.annotation.IdRes import androidx.appcompat.view.ActionMode import androidx.core.app.ActivityOptionsCompat @@ -17,20 +18,19 @@ import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.commit import androidx.lifecycle.lifecycleScope import androidx.transition.TransitionManager +import com.google.android.material.R as materialR import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.LayoutParams.* import com.google.android.material.navigation.NavigationBarView import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.coroutines.yield -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.base.ui.widgets.KotatsuBottomNavigationView -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ActivityMainBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.explore.ui.ExploreFragment @@ -48,16 +48,15 @@ import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment import org.koitharu.kotatsu.settings.tools.ToolsFragment import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker -import org.koitharu.kotatsu.sync.domain.SyncController import org.koitharu.kotatsu.tracker.ui.FeedFragment import org.koitharu.kotatsu.tracker.work.TrackWorker import org.koitharu.kotatsu.utils.VoiceInputContract import org.koitharu.kotatsu.utils.ext.* -import com.google.android.material.R as materialR private const val TAG_PRIMARY = "primary" private const val TAG_SEARCH = "search" +@AndroidEntryPoint class MainActivity : BaseActivity(), AppBarOwner, @@ -68,8 +67,8 @@ class MainActivity : NavigationBarView.OnItemSelectedListener, NavigationBarView.OnItemReselectedListener { - private val viewModel by viewModel() - private val searchSuggestionViewModel by viewModel() + private val viewModel by viewModels() + private val searchSuggestionViewModel by viewModels() private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback()) private lateinit var navBar: NavigationBarView @@ -284,7 +283,8 @@ class MainActivity : } private fun onError(e: Throwable) { - Snackbar.make(binding.container, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).setAnchorView(bottomNav).show() + Snackbar.make(binding.container, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).setAnchorView(bottomNav) + .show() } private fun onCountersChanged(counters: SparseIntArray) { @@ -366,13 +366,12 @@ class MainActivity : TrackWorker.setup(applicationContext) SuggestionsWorker.setup(applicationContext) } - val settings = get() when { !settings.isSourcesSelected -> OnboardDialogFragment.showWelcome(supportFragmentManager) settings.newSources.isNotEmpty() -> NewSourcesDialogFragment.show(supportFragmentManager) } yield() - get().requestFullSyncAndGc(get()) + // TODO get().requestFullSyncAndGc(get()) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt index 1362ff449..c60ae4aa8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.main.ui import android.util.SparseIntArray import androidx.core.util.set import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R @@ -15,8 +16,10 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct +import javax.inject.Inject -class MainViewModel( +@HiltViewModel +class MainViewModel @Inject constructor( private val historyRepository: HistoryRepository, private val settings: AppSettings, private val appUpdateRepository: AppUpdateRepository, diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt index a92d866d2..a3b4bcbbd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt @@ -4,9 +4,12 @@ import android.app.Activity import android.app.Application import android.content.Intent import android.os.Bundle +import javax.inject.Inject +import javax.inject.Singleton import org.koitharu.kotatsu.core.prefs.AppSettings -class AppProtectHelper(private val settings: AppSettings) : Application.ActivityLifecycleCallbacks { +@Singleton +class AppProtectHelper @Inject constructor(private val settings: AppSettings) : Application.ActivityLifecycleCallbacks { private var isUnlocked = settings.appPassword.isNullOrEmpty() @@ -46,4 +49,4 @@ class AppProtectHelper(private val settings: AppSettings) : Application.Activity private fun restoreLock() { isUnlocked = settings.appPassword.isNullOrEmpty() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt index 0da2ee55f..b3d0322bf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt @@ -10,25 +10,27 @@ import android.view.View import android.view.WindowManager import android.view.inputmethod.EditorInfo import android.widget.TextView +import androidx.activity.viewModels import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt.AuthenticationCallback import androidx.core.graphics.Insets -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityProtectBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage +@AndroidEntryPoint class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWatcher, View.OnClickListener { - private val viewModel by viewModel() + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -58,7 +60,7 @@ class ProtectActivity : basePadding + insets.left, basePadding + insets.top, basePadding + insets.right, - basePadding + insets.bottom + basePadding + insets.bottom, ) } @@ -129,4 +131,4 @@ class ProtectActivity : .putExtra(EXTRA_INTENT, sourceIntent) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt index 85ffe23cb..a55d15c84 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.main.ui.protect +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.delay import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -10,7 +12,8 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent private const val PASSWORD_COMPARE_DELAY = 1_000L -class ProtectViewModel( +@HiltViewModel +class ProtectViewModel @Inject constructor( private val settings: AppSettings, private val protectHelper: AppProtectHelper, ) : BaseViewModel() { @@ -42,4 +45,4 @@ class ProtectViewModel( protectHelper.unlock() onUnlockSuccess.call(Unit) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ReaderModule.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ReaderModule.kt deleted file mode 100644 index 5b8faf203..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ReaderModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.koitharu.kotatsu.reader - -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.base.domain.MangaDataRepository -import org.koitharu.kotatsu.local.data.PagesCache -import org.koitharu.kotatsu.reader.ui.PageSaveHelper -import org.koitharu.kotatsu.reader.ui.ReaderViewModel - -val readerModule - get() = module { - - factory { MangaDataRepository(get()) } - single { PagesCache(get()) } - - factory { PageSaveHelper(get(), androidContext()) } - - viewModel { params -> - ReaderViewModel( - intent = params[0], - initialState = params[1], - preselectedBranch = params[2], - dataRepository = get(), - historyRepository = get(), - settings = get(), - pageSaveHelper = get(), - bookmarksRepository = get(), - ) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/ChaptersLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/ChaptersLoader.kt index 75c096296..7d3649268 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/ChaptersLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/ChaptersLoader.kt @@ -10,7 +10,9 @@ import org.koitharu.kotatsu.reader.ui.pager.ReaderPage private const val PAGES_TRIM_THRESHOLD = 120 -class ChaptersLoader { +class ChaptersLoader( + private val mangaRepositoryFactory: MangaRepository.Factory, +) { val chapters = LongSparseArray() private val chapterPages = ChapterPages() @@ -62,7 +64,7 @@ class ChaptersLoader { private suspend fun loadChapter(manga: Manga, chapterId: Long): List { val chapter = checkNotNull(chapters[chapterId]) { "Requested chapter not found" } - val repo = MangaRepository(manga.source) + val repo = mangaRepositoryFactory.create(manga.source) return repo.getPages(chapter).mapIndexed { index, page -> ReaderPage(page, index, chapterId) } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 696f48cc3..91332872b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -6,6 +6,12 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.collection.LongSparseArray import androidx.collection.set +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import java.util.* +import java.util.concurrent.atomic.AtomicInteger +import java.util.zip.ZipFile +import javax.inject.Inject import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,8 +20,6 @@ import kotlinx.coroutines.sync.withLock import okhttp3.OkHttpClient import okhttp3.Request import okio.Closeable -import org.koin.core.component.KoinComponent -import org.koin.core.component.get import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository @@ -27,26 +31,25 @@ import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.utils.ext.connectivityManager import org.koitharu.kotatsu.utils.progress.ProgressDeferred -import java.io.File -import java.util.* -import java.util.concurrent.atomic.AtomicInteger -import java.util.zip.ZipFile private const val PROGRESS_UNDEFINED = -1f private const val PREFETCH_LIMIT_DEFAULT = 10 -class PageLoader : KoinComponent, Closeable { +class PageLoader @Inject constructor( + private val okHttp: OkHttpClient, + private val cache: PagesCache, + private val settings: AppSettings, + @ApplicationContext context: Context, + private val mangaRepositoryFactory: MangaRepository.Factory, +) : Closeable { val loaderScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) - private val okHttp = get() - private val cache = get() - private val settings = get() - private val connectivityManager = get().connectivityManager + private val connectivityManager = context.connectivityManager private val tasks = LongSparseArray>() private val convertLock = Mutex() private var repository: MangaRepository? = null - private var prefetchQueue = LinkedList() + private val prefetchQueue = LinkedList() private val counter = AtomicInteger(0) private var prefetchQueueLimit = PREFETCH_LIMIT_DEFAULT // TODO adaptive private val emptyProgressFlow: StateFlow = MutableStateFlow(-1f) @@ -150,7 +153,7 @@ class PageLoader : KoinComponent, Closeable { return if (result != null && result.source == source) { result } else { - MangaRepository(source).also { repository = it } + mangaRepositoryFactory.create(source).also { repository = it } } } @@ -194,4 +197,4 @@ class PageLoader : KoinComponent, Closeable { val deferred = CompletableDeferred(file) return ProgressDeferred(deferred, emptyProgressFlow) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt index 963ec3029..7d4eb83e1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt @@ -5,8 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager -import kotlin.math.roundToInt -import org.koin.android.ext.android.get +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -19,9 +18,15 @@ import org.koitharu.kotatsu.details.ui.model.toListItem import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback import org.koitharu.kotatsu.utils.ext.withArgs +import javax.inject.Inject +import kotlin.math.roundToInt +@AndroidEntryPoint class ChaptersBottomSheet : BaseBottomSheet(), OnListItemClickListener { + @Inject + lateinit var settings: AppSettings + override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetChaptersBinding { return SheetChaptersBinding.inflate(inflater, container, false) } @@ -35,7 +40,7 @@ class ChaptersBottomSheet : BaseBottomSheet(), OnListItemC } val currentId = requireArguments().getLong(ARG_CURRENT_ID, 0L) val currentPosition = chapters.indexOfFirst { it.id == currentId } - val dateFormat = get().getDateFormat() + val dateFormat = settings.getDateFormat() val items = chapters.mapIndexed { index, chapter -> chapter.toListItem( isCurrent = index == currentPosition, diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt index 3e19c7036..1ba72f858 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt @@ -4,27 +4,27 @@ import android.content.Context import android.net.Uri import android.webkit.MimeTypeMap import androidx.activity.result.ActivityResultLauncher +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import javax.inject.Inject +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import okhttp3.HttpUrl.Companion.toHttpUrl import okio.IOException -import org.koitharu.kotatsu.base.domain.MangaUtils -import org.koitharu.kotatsu.local.data.PagesCache +import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.toFileNameSafe import org.koitharu.kotatsu.reader.domain.PageLoader -import java.io.File -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume private const val MAX_FILENAME_LENGTH = 10 private const val EXTENSION_FALLBACK = "png" -class PageSaveHelper( - private val cache: PagesCache, - context: Context, +class PageSaveHelper @Inject constructor( + @ApplicationContext context: Context, ) { private var continuation: Continuation? = null @@ -65,7 +65,7 @@ class PageSaveHelper( var extension = name.substringAfterLast('.', "") name = name.substringBeforeLast('.') if (extension.length !in 2..4) { - val mimeType = MangaUtils.getImageMimeType(file) + val mimeType = MangaDataRepository.getImageMimeType(file) extension = if (mimeType != null) { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: EXTENSION_FALLBACK } else { @@ -74,4 +74,4 @@ class PageSaveHelper( } return name.toFileNameSafe().take(MAX_FILENAME_LENGTH) + "." + extension } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index c8f2ae510..4c0d23b81 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.* -import androidx.activity.result.ActivityResultCallback import androidx.core.graphics.Insets import androidx.core.view.OnApplyWindowInsetsListener import androidx.core.view.WindowInsetsCompat @@ -18,12 +17,12 @@ import androidx.transition.TransitionManager import androidx.transition.TransitionSet import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import java.util.concurrent.TimeUnit +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent @@ -44,8 +43,8 @@ import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.utils.GridTouchHelper import org.koitharu.kotatsu.utils.ShareHelper import org.koitharu.kotatsu.utils.ext.* -import java.util.concurrent.TimeUnit +@AndroidEntryPoint class ReaderActivity : BaseFullscreenActivity(), ChaptersBottomSheet.OnChapterChangeListener, @@ -55,11 +54,14 @@ class ReaderActivity : ReaderControlDelegate.OnInteractionListener, OnApplyWindowInsetsListener { - private val viewModel by viewModel { - parametersOf( - MangaIntent(intent), - intent?.getParcelableExtra(EXTRA_STATE), - intent?.getStringExtra(EXTRA_BRANCH), + @Inject + lateinit var viewModelFactory: ReaderViewModel.Factory + + val viewModel by assistedViewModels { + viewModelFactory.create( + intent = MangaIntent(intent), + initialState = intent?.getParcelableExtra(EXTRA_STATE), + preselectedBranch = intent?.getStringExtra(EXTRA_BRANCH), ) } @@ -75,7 +77,7 @@ class ReaderActivity : readerManager = ReaderManager(supportFragmentManager, R.id.container) supportActionBar?.setDisplayHomeAsUpEnabled(true) touchHelper = GridTouchHelper(this, this) - controlDelegate = ReaderControlDelegate(lifecycleScope, get(), this) + controlDelegate = ReaderControlDelegate(lifecycleScope, settings, this) binding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected) binding.slider.setLabelFormatter(PageLabelFormatter()) ReaderSliderListener(this, viewModel).attachToSlider(binding.slider) @@ -121,7 +123,7 @@ class ReaderActivity : ChaptersBottomSheet.show( supportFragmentManager, viewModel.manga?.chapters.orEmpty(), - viewModel.getCurrentState()?.chapterId ?: 0L + viewModel.getCurrentState()?.chapterId ?: 0L, ) } R.id.action_pages_thumbs -> { @@ -284,12 +286,12 @@ class ReaderActivity : binding.appbarTop.updatePadding( top = systemBars.top, right = systemBars.right, - left = systemBars.left + left = systemBars.left, ) binding.appbarBottom?.updatePadding( bottom = systemBars.bottom, right = systemBars.right, - left = systemBars.left + left = systemBars.left, ) return WindowInsetsCompat.Builder(insets) .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index 2a6453d51..1ea0e1313 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -7,12 +7,16 @@ import androidx.annotation.AnyThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import java.util.* +import javax.inject.Provider import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.base.domain.MangaIntent -import org.koitharu.kotatsu.base.domain.MangaUtils import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository @@ -33,20 +37,21 @@ import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.processLifecycleScope import org.koitharu.kotatsu.utils.ext.requireValue -import java.util.* private const val BOUNDS_PAGE_OFFSET = 2 private const val PREFETCH_LIMIT = 10 -class ReaderViewModel( - private val intent: MangaIntent, - initialState: ReaderState?, - private val preselectedBranch: String?, +class ReaderViewModel @AssistedInject constructor( + @Assisted private val intent: MangaIntent, + @Assisted initialState: ReaderState?, + @Assisted private val preselectedBranch: String?, + private val mangaRepositoryFactory: MangaRepository.Factory, private val dataRepository: MangaDataRepository, private val historyRepository: HistoryRepository, private val bookmarksRepository: BookmarksRepository, private val settings: AppSettings, private val pageSaveHelper: PageSaveHelper, + pageLoaderFactory: Provider, ) : BaseViewModel() { private var loadingJob: Job? = null @@ -57,8 +62,8 @@ class ReaderViewModel( private val chapters: LongSparseArray get() = chaptersLoader.chapters - val pageLoader = PageLoader() - private val chaptersLoader = ChaptersLoader() + val pageLoader = pageLoaderFactory.get() + private val chaptersLoader = ChaptersLoader(mangaRepositoryFactory) val readerMode = MutableLiveData() val onPageSaved = SingleLiveEvent() @@ -72,7 +77,7 @@ class ReaderViewModel( val readerAnimation = settings.observeAsLiveData( context = viewModelScope.coroutineContext + Dispatchers.Default, key = AppSettings.KEY_READER_ANIMATION, - valueProducer = { readerAnimation } + valueProducer = { readerAnimation }, ) val isScreenshotsBlockEnabled = combine( @@ -115,12 +120,12 @@ class ReaderViewModel( val manga = checkNotNull(mangaData.value) dataRepository.savePreferences( manga = manga, - mode = newMode + mode = newMode, ) readerMode.value = newMode content.value?.run { content.value = copy( - state = getCurrentState() + state = getCurrentState(), ) } } @@ -253,7 +258,7 @@ class ReaderViewModel( loadingJob = launchLoadingJob(Dispatchers.Default) { var manga = dataRepository.resolveIntent(intent) ?: throw NotFoundException("Cannot find manga", "") mangaData.value = manga - val repo = MangaRepository(manga.source) + val repo = mangaRepositoryFactory.create(manga.source) manga = repo.getDetails(manga) manga.chapters?.forEach { chapters.put(it.id, it) @@ -317,7 +322,7 @@ class ReaderViewModel( ?: error("There are no chapters in this manga") val pages = repo.getPages(chapter) return runCatching { - val isWebtoon = MangaUtils.determineMangaIsWebtoon(pages) + val isWebtoon = dataRepository.determineMangaIsWebtoon(repo, pages) if (isWebtoon) ReaderMode.WEBTOON else defaultMode }.onSuccess { dataRepository.savePreferences(manga, it) @@ -353,6 +358,16 @@ class ReaderViewModel( val ppc = 1f / chaptersCount return ppc * chapterIndex + ppc * pagePercent } + + @AssistedFactory + interface Factory { + + fun create( + intent: MangaIntent, + initialState: ReaderState?, + preselectedBranch: String?, + ): ReaderViewModel + } } /** diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt index 46a6663af..02767a590 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt @@ -8,10 +8,10 @@ import android.view.ViewGroup import androidx.activity.result.ActivityResultCallback import androidx.core.view.isGone import androidx.fragment.app.FragmentManager +import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup @@ -30,7 +30,7 @@ class ReaderConfigBottomSheet : ActivityResultCallback, View.OnClickListener { - private val viewModel by sharedViewModel() + private val viewModel by activityViewModels() private val savePageRequest = registerForActivityResult(PageSaveContract(), this) private var orientationHelper: ScreenOrientationHelper? = null private lateinit var mode: ReaderMode diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReader.kt index 7b7c4d498..52894b4da 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReader.kt @@ -3,8 +3,8 @@ package org.koitharu.kotatsu.reader.ui.pager import android.os.Bundle import android.view.View import androidx.core.graphics.Insets +import androidx.fragment.app.activityViewModels import androidx.viewbinding.ViewBinding -import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderViewModel @@ -13,7 +13,7 @@ private const val KEY_STATE = "state" abstract class BaseReader : BaseFragment() { - protected val viewModel by sharedViewModel() + protected val viewModel by activityViewModels() private var stateToSave: ReaderState? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -53,4 +53,4 @@ abstract class BaseReader : BaseFragment() { abstract fun getCurrentState(): ReaderState? protected abstract fun onPagesChanged(pages: List, pendingState: ReaderState?) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt index d43f6aee3..30a8b7331 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt @@ -6,9 +6,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.children +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlin.math.absoluteValue import kotlinx.coroutines.async -import org.koin.android.ext.android.get +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -20,19 +22,23 @@ import org.koitharu.kotatsu.utils.ext.recyclerView import org.koitharu.kotatsu.utils.ext.resetTransformations import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +@AndroidEntryPoint class ReversedReaderFragment : BaseReader() { + @Inject + lateinit var settings: AppSettings + private var pagerAdapter: ReversedPagesAdapter? = null override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentReaderStandardBinding.inflate(inflater, container, false) @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - pagerAdapter = ReversedPagesAdapter(viewModel.pageLoader, get(), exceptionResolver) + pagerAdapter = ReversedPagesAdapter(viewModel.pageLoader, settings, exceptionResolver) with(binding.pager) { adapter = pagerAdapter offscreenPageLimit = 2 @@ -67,7 +73,7 @@ class ReversedReaderFragment : BaseReader() { override fun switchPageTo(position: Int, smooth: Boolean) { binding.pager.setCurrentItem( reversed(position), - smooth && (binding.pager.currentItem - position).absoluteValue < PagerReaderFragment.SMOOTH_SCROLL_LIMIT + smooth && (binding.pager.currentItem - position).absoluteValue < PagerReaderFragment.SMOOTH_SCROLL_LIMIT, ) } @@ -98,7 +104,7 @@ class ReversedReaderFragment : BaseReader() { ReaderState( chapterId = page.chapterId, page = page.index, - scroll = 0 + scroll = 0, ) } @@ -109,4 +115,4 @@ class ReversedReaderFragment : BaseReader() { private fun reversed(position: Int): Int { return ((pagerAdapter?.itemCount ?: 0) - position - 1).coerceAtLeast(0) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt index a0a53b2d1..bb3a583c8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt @@ -6,9 +6,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.children +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlin.math.absoluteValue import kotlinx.coroutines.async -import org.koin.android.ext.android.get +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -19,19 +21,23 @@ import org.koitharu.kotatsu.utils.ext.recyclerView import org.koitharu.kotatsu.utils.ext.resetTransformations import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +@AndroidEntryPoint class PagerReaderFragment : BaseReader() { + @Inject + lateinit var settings: AppSettings + private var pagesAdapter: PagesAdapter? = null override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentReaderStandardBinding.inflate(inflater, container, false) @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - pagesAdapter = PagesAdapter(viewModel.pageLoader, get(), exceptionResolver) + pagesAdapter = PagesAdapter(viewModel.pageLoader, settings, exceptionResolver) with(binding.pager) { adapter = pagesAdapter offscreenPageLimit = 2 @@ -86,7 +92,7 @@ class PagerReaderFragment : BaseReader() { override fun switchPageTo(position: Int, smooth: Boolean) { binding.pager.setCurrentItem( position, - smooth && (binding.pager.currentItem - position).absoluteValue < SMOOTH_SCROLL_LIMIT + smooth && (binding.pager.currentItem - position).absoluteValue < SMOOTH_SCROLL_LIMIT, ) } @@ -96,7 +102,7 @@ class PagerReaderFragment : BaseReader() { ReaderState( chapterId = page.chapterId, page = page.index, - scroll = 0 + scroll = 0, ) } @@ -108,4 +114,4 @@ class PagerReaderFragment : BaseReader() { const val SMOOTH_SCROLL_LIMIT = 3 } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt index d0c25311d..cfdf3e9c5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt @@ -5,8 +5,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateDecelerateInterpolator +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.async -import org.koin.android.ext.android.get +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -16,19 +18,23 @@ import org.koitharu.kotatsu.utils.ext.findCenterViewPosition import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +@AndroidEntryPoint class WebtoonReaderFragment : BaseReader() { + @Inject + lateinit var settings: AppSettings + private val scrollInterpolator = AccelerateDecelerateInterpolator() private var webtoonAdapter: WebtoonAdapter? = null override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentReaderWebtoonBinding.inflate(inflater, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - webtoonAdapter = WebtoonAdapter(viewModel.pageLoader, get(), exceptionResolver) + webtoonAdapter = WebtoonAdapter(viewModel.pageLoader, settings, exceptionResolver) with(binding.recyclerView) { setHasFixedSize(true) adapter = webtoonAdapter @@ -73,7 +79,7 @@ class WebtoonReaderFragment : BaseReader() { chapterId = page.chapterId, page = page.index, scroll = (recyclerView.findViewHolderForAdapterPosition(currentItem) as? WebtoonHolder) - ?.getScrollY() ?: 0 + ?.getScrollY() ?: 0, ) } @@ -85,7 +91,7 @@ class WebtoonReaderFragment : BaseReader() { binding.recyclerView.smoothScrollBy( 0, (binding.recyclerView.height * 0.9).toInt() * delta, - scrollInterpolator + scrollInterpolator, ) } @@ -100,4 +106,4 @@ class WebtoonReaderFragment : BaseReader() { notifyPageChanged(index) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt index a85c10a5c..cb608d06d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt @@ -6,8 +6,10 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.GridLayoutManager -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.getViewModel +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +import javax.inject.Provider import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -21,16 +23,28 @@ import org.koitharu.kotatsu.list.ui.MangaListSpanResolver import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.ui.ReaderActivity -import org.koitharu.kotatsu.reader.ui.ReaderViewModel import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter import org.koitharu.kotatsu.utils.ext.viewLifecycleScope import org.koitharu.kotatsu.utils.ext.withArgs +@AndroidEntryPoint class PagesThumbnailsSheet : BaseBottomSheet(), OnListItemClickListener, BottomSheetHeaderBar.OnExpansionChangeListener { + @Inject + lateinit var mangaRepositoryFactory: MangaRepository.Factory + + @Inject + lateinit var pageLoaderProvider: Provider + + @Inject + lateinit var coil: ImageLoader + + @Inject + lateinit var settings: AppSettings + private lateinit var thumbnails: List private var spanResolver: MangaListSpanResolver? = null private var currentPageIndex = -1 @@ -44,7 +58,7 @@ class PagesThumbnailsSheet : return } currentPageIndex = requireArguments().getInt(ARG_CURRENT, currentPageIndex) - val repository = MangaRepository(pages.first().source) + val repository = mangaRepositoryFactory.create(pages.first().source) thumbnails = pages.mapIndexed { i, x -> PageThumbnail( number = i + 1, @@ -75,13 +89,13 @@ class PagesThumbnailsSheet : ) adapter = PageThumbnailAdapter( dataSet = thumbnails, - coil = get(), + coil = coil, scope = viewLifecycleScope, loader = getPageLoader(), clickListener = this@PagesThumbnailsSheet, ) addOnLayoutChangeListener(spanResolver) - spanResolver?.setGridSize(get().gridSize / 100f, this) + spanResolver?.setGridSize(settings.gridSize / 100f, this) if (currentPageIndex > 0) { val offset = resources.getDimensionPixelOffset(R.dimen.preferred_grid_width) (layoutManager as GridLayoutManager).scrollToPositionWithOffset(currentPageIndex, offset) @@ -119,8 +133,8 @@ class PagesThumbnailsSheet : } private fun getPageLoader(): PageLoader { - val viewModel = (activity as? ReaderActivity)?.getViewModel() - return viewModel?.pageLoader ?: PageLoader().also { pageLoader = it } + val viewModel = (activity as? ReaderActivity)?.viewModel + return viewModel?.pageLoader ?: pageLoaderProvider.get().also { pageLoader = it } } companion object { diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/RemoteListModule.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/RemoteListModule.kt deleted file mode 100644 index c6b81174a..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/RemoteListModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.remotelist - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.core.parser.RemoteMangaRepository -import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel - -val remoteListModule - get() = module { - - viewModel { params -> - RemoteListViewModel( - repository = MangaRepository(params[0]) as RemoteMangaRepository, - settings = get(), - dataRepository = get(), - searchRepository = get(), - ) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt index d7ac7a8bf..f653f86f6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt @@ -8,8 +8,8 @@ import android.view.View import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.view.MenuProvider -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.list.ui.MangaListFragment @@ -19,13 +19,18 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.search.ui.SearchActivity import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.utils.ext.addMenuProvider +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.serializableArgument import org.koitharu.kotatsu.utils.ext.withArgs +@AndroidEntryPoint class RemoteListFragment : MangaListFragment() { - override val viewModel by viewModel { - parametersOf(source) + @Inject + lateinit var viewModelFactory: RemoteListViewModel.Factory + + public override val viewModel by assistedViewModels { + viewModelFactory.create(source) } private val source by serializableArgument(ARG_SOURCE) diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index 0c4fdd362..53ed57bca 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -2,6 +2,10 @@ package org.koitharu.kotatsu.remotelist.ui import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import java.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin @@ -9,6 +13,7 @@ import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.base.ui.widgets.ChipsView +import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaListViewModel @@ -18,21 +23,23 @@ import org.koitharu.kotatsu.list.ui.filter.FilterState import org.koitharu.kotatsu.list.ui.filter.OnFilterChangedListener import org.koitharu.kotatsu.list.ui.model.* import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.search.domain.MangaSearchRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.printStackTraceDebug -import java.util.* private const val FILTER_MIN_INTERVAL = 250L -class RemoteListViewModel( - private val repository: RemoteMangaRepository, +class RemoteListViewModel @AssistedInject constructor( + @Assisted source: MangaSource, + mangaRepositoryFactory: MangaRepository.Factory, private val searchRepository: MangaSearchRepository, settings: AppSettings, dataRepository: MangaDataRepository, ) : MangaListViewModel(settings), OnFilterChangedListener { + private val repository = mangaRepositoryFactory.create(source) as RemoteMangaRepository private val filter = FilterCoordinator(repository, dataRepository, viewModelScope) private val mangaList = MutableStateFlow?>(null) private val hasNextPage = MutableStateFlow(false) @@ -158,7 +165,7 @@ class RemoteListViewModel( private suspend fun createChipsList( filterState: FilterState, - availableTags: Set + availableTags: Set, ): List { val selectedTags = filterState.tags.toMutableSet() var tags = searchRepository.getTagsSuggestion("", 6, repository.source) @@ -195,4 +202,10 @@ class RemoteListViewModel( } return result } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(source: MangaSource): RemoteListViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt new file mode 100644 index 000000000..6f1b5592c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt @@ -0,0 +1,41 @@ +package org.koitharu.kotatsu.scrobbling + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ElementsIntoSet +import okhttp3.OkHttpClient +import org.koitharu.kotatsu.core.db.MangaDatabase +import org.koitharu.kotatsu.scrobbling.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriAuthenticator +import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriInterceptor +import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository +import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriStorage +import org.koitharu.kotatsu.scrobbling.shikimori.domain.ShikimoriScrobbler +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ScrobblingModule { + + @Provides + @Singleton + fun provideShikimoriRepository( + storage: ShikimoriStorage, + database: MangaDatabase, + authenticator: ShikimoriAuthenticator, + ): ShikimoriRepository { + val okHttp = OkHttpClient.Builder().apply { + authenticator(authenticator) + addInterceptor(ShikimoriInterceptor(storage)) + }.build() + return ShikimoriRepository(okHttp, storage, database) + } + + @Provides + @ElementsIntoSet + fun provideScrobblers( + shikimoriScrobbler: ShikimoriScrobbler, + ): Set<@JvmSuppressWildcards Scrobbler> = setOf(shikimoriScrobbler) +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ShikimoriModule.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ShikimoriModule.kt deleted file mode 100644 index ec3c65b57..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ShikimoriModule.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.shikimori - -import okhttp3.OkHttpClient -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriAuthenticator -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriInterceptor -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriStorage -import org.koitharu.kotatsu.scrobbling.shikimori.domain.ShikimoriScrobbler -import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsViewModel -import org.koitharu.kotatsu.scrobbling.ui.selector.ScrobblingSelectorViewModel - -val shikimoriModule - get() = module { - single { ShikimoriStorage(androidContext()) } - factory { - val okHttp = OkHttpClient.Builder().apply { - authenticator(ShikimoriAuthenticator(get(), ::get)) - addInterceptor(ShikimoriInterceptor(get())) - }.build() - ShikimoriRepository(okHttp, get(), get()) - } - factory { ShikimoriScrobbler(get(), get()) } bind Scrobbler::class - viewModel { params -> - ShikimoriSettingsViewModel(get(), params.getOrNull()) - } - viewModel { params -> ScrobblingSelectorViewModel(params[0], get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriAuthenticator.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriAuthenticator.kt index 8a94bf98a..7b683cf1d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriAuthenticator.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriAuthenticator.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.scrobbling.shikimori.data +import javax.inject.Inject +import javax.inject.Provider import kotlinx.coroutines.runBlocking import okhttp3.Authenticator import okhttp3.Request @@ -8,9 +10,9 @@ import okhttp3.Route import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.network.CommonHeaders -class ShikimoriAuthenticator( +class ShikimoriAuthenticator @Inject constructor( private val storage: ShikimoriStorage, - private val repositoryProvider: () -> ShikimoriRepository, + private val repositoryProvider: Provider, ) : Authenticator { override fun authenticate(route: Route?, response: Response): Request? { @@ -40,7 +42,7 @@ class ShikimoriAuthenticator( } private fun refreshAccessToken(): String? = runCatching { - val repository = repositoryProvider() + val repository = repositoryProvider.get() runBlocking { repository.authorize(null) } return storage.accessToken }.onFailure { @@ -48,4 +50,4 @@ class ShikimoriAuthenticator( it.printStackTrace() } }.getOrNull() -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt index 119d33637..2fcd12718 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt @@ -103,7 +103,7 @@ class ShikimoriRepository( put("target_id", shikiMangaId) put("target_type", "Manga") put("user_id", user.id) - } + }, ) val url = BASE_URL.toHttpUrl().newBuilder() .addPathSegment("api") @@ -121,7 +121,7 @@ class ShikimoriRepository( "user_rate", JSONObject().apply { put("chapters", chapter.number) - } + }, ) val url = BASE_URL.toHttpUrl().newBuilder() .addPathSegment("api") @@ -146,7 +146,7 @@ class ShikimoriRepository( if (status != null) { put("status", status) } - } + }, ) val url = BASE_URL.toHttpUrl().newBuilder() .addPathSegment("api") @@ -196,4 +196,4 @@ class ShikimoriRepository( url = json.getString("url").toAbsoluteUrl("shikimori.one"), descriptionHtml = json.getString("description_html"), ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriStorage.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriStorage.kt index 0dfe0421e..a3cd84e85 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriStorage.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriStorage.kt @@ -2,15 +2,19 @@ package org.koitharu.kotatsu.scrobbling.shikimori.data import android.content.Context import androidx.core.content.edit +import dagger.hilt.android.qualifiers.ApplicationContext import org.json.JSONObject import org.koitharu.kotatsu.scrobbling.shikimori.data.model.ShikimoriUser +import javax.inject.Inject +import javax.inject.Singleton private const val PREF_NAME = "shikimori" private const val KEY_ACCESS_TOKEN = "access_token" private const val KEY_REFRESH_TOKEN = "refresh_token" private const val KEY_USER = "user" -class ShikimoriStorage(context: Context) { +@Singleton +class ShikimoriStorage @Inject constructor(@ApplicationContext context: Context) { private val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) @@ -33,4 +37,4 @@ class ShikimoriStorage(context: Context) { fun clear() = prefs.edit { clear() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/domain/ShikimoriScrobbler.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/domain/ShikimoriScrobbler.kt index 72f9d5cbf..bf69e3fa4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/domain/ShikimoriScrobbler.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/domain/ShikimoriScrobbler.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.scrobbling.shikimori.domain +import javax.inject.Inject +import javax.inject.Singleton import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.scrobbling.domain.Scrobbler @@ -11,7 +13,8 @@ import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository private const val RATING_MAX = 10f -class ShikimoriScrobbler( +@Singleton +class ShikimoriScrobbler @Inject constructor( private val repository: ShikimoriRepository, db: MangaDatabase, ) : Scrobbler(db, ScrobblerService.SHIKIMORI) { @@ -65,4 +68,4 @@ class ShikimoriScrobbler( override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { return repository.getMangaInfo(id) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt index 10098a239..6588ef675 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt @@ -8,22 +8,26 @@ import androidx.preference.Preference import coil.ImageLoader import coil.request.ImageRequest import coil.transform.CircleCropTransformation -import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.scrobbling.shikimori.data.model.ShikimoriUser import org.koitharu.kotatsu.utils.PreferenceIconTarget +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.withArgs class ShikimoriSettingsFragment : BasePreferenceFragment(R.string.shikimori) { - private val viewModel by viewModel { - parametersOf(arguments?.getString(ARG_AUTH_CODE)) + @Inject + lateinit var coil: ImageLoader + + @Inject + lateinit var viewModelFactory: ShikimoriSettingsViewModel.Factory + + private val viewModel by assistedViewModels { + viewModelFactory.create(arguments?.getString(ARG_AUTH_CODE)) } - private val coil by inject(mode = LazyThreadSafetyMode.NONE) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_shikimori) @@ -76,4 +80,4 @@ class ShikimoriSettingsFragment : BasePreferenceFragment(R.string.shikimori) { putString(ARG_AUTH_CODE, authCode) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt index ef8f73b85..091bdcea3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt @@ -1,14 +1,17 @@ package org.koitharu.kotatsu.scrobbling.shikimori.ui import androidx.lifecycle.MutableLiveData +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository import org.koitharu.kotatsu.scrobbling.shikimori.data.model.ShikimoriUser -class ShikimoriSettingsViewModel( +class ShikimoriSettingsViewModel @AssistedInject constructor( private val repository: ShikimoriRepository, - authCode: String?, + @Assisted authCode: String?, ) : BaseViewModel() { val authorizationUrl: String @@ -45,4 +48,10 @@ class ShikimoriSettingsViewModel( repository.authorize(code) user.postValue(repository.loadUser()) } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(authCode: String?): ShikimoriSettingsViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt index cf2d70bb3..1f58131c4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt @@ -7,9 +7,8 @@ import android.view.* import android.widget.Toast import androidx.appcompat.widget.SearchView import androidx.fragment.app.FragmentManager -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import coil.ImageLoader +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.ui.BaseBottomSheet @@ -21,6 +20,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ShikiMangaSelectionDecoration import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ShikimoriSelectorAdapter +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.withArgs @@ -33,8 +33,16 @@ class ScrobblingSelectorBottomSheet : SearchView.OnQueryTextListener, DialogInterface.OnKeyListener { - private val viewModel by viewModel { - parametersOf(requireNotNull(requireArguments().getParcelable(MangaIntent.KEY_MANGA)).manga) + @Inject + lateinit var viewModelFactory: ScrobblingSelectorViewModel.Factory + + @Inject + lateinit var coil: ImageLoader + + private val viewModel by assistedViewModels { + viewModelFactory.create( + requireNotNull(requireArguments().getParcelable(MangaIntent.KEY_MANGA)).manga, + ) } override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetScrobblingSelectorBinding { @@ -49,7 +57,7 @@ class ScrobblingSelectorBottomSheet : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val listAdapter = ShikimoriSelectorAdapter(viewLifecycleOwner, get(), this) + val listAdapter = ShikimoriSelectorAdapter(viewLifecycleOwner, coil, this) val decoration = ShikiMangaSelectionDecoration(view.context) with(binding.recyclerView) { adapter = listAdapter diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt index 2c881b23b..5a121f52c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt @@ -4,6 +4,9 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.recyclerview.widget.RecyclerView.NO_ID +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -19,8 +22,8 @@ import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class ScrobblingSelectorViewModel( - val manga: Manga, +class ScrobblingSelectorViewModel @AssistedInject constructor( + @Assisted val manga: Manga, private val scrobbler: Scrobbler, ) : BaseViewModel() { @@ -31,7 +34,7 @@ class ScrobblingSelectorViewModel( val content: LiveData> = combine( shikiMangaList.filterNotNull(), - hasNextPage + hasNextPage, ) { list, isHasNextPage -> when { list.isEmpty() -> listOf() @@ -98,4 +101,10 @@ class ScrobblingSelectorViewModel( onClose.postCall(Unit) } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(manga: Manga): ScrobblingSelectorViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/SearchModule.kt b/app/src/main/java/org/koitharu/kotatsu/search/SearchModule.kt deleted file mode 100644 index b06e06bfd..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/search/SearchModule.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.koitharu.kotatsu.search - -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.search.domain.MangaSearchRepository -import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider -import org.koitharu.kotatsu.search.ui.SearchViewModel -import org.koitharu.kotatsu.search.ui.multi.MultiSearchViewModel -import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel - -val searchModule - get() = module { - - factory { MangaSearchRepository(get(), get(), androidContext(), get()) } - factory { MangaSuggestionsProvider.createSuggestions(androidContext()) } - - viewModel { params -> SearchViewModel(MangaRepository(params[0]), params[1], get()) } - viewModel { SearchSuggestionViewModel(get(), get()) } - viewModel { params -> MultiSearchViewModel(params[0], get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt b/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt index 0d29da468..1725620c4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt @@ -4,6 +4,8 @@ import android.annotation.SuppressLint import android.app.SearchManager import android.content.Context import android.provider.SearchRecentSuggestions +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.* @@ -20,18 +22,19 @@ import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.util.levenshteinDistance import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider -class MangaSearchRepository( +class MangaSearchRepository @Inject constructor( private val settings: AppSettings, private val db: MangaDatabase, - private val context: Context, + @ApplicationContext private val context: Context, private val recentSuggestions: SearchRecentSuggestions, + private val mangaRepositoryFactory: MangaRepository.Factory, ) { fun globalSearch(query: String, concurrency: Int = DEFAULT_CONCURRENCY): Flow = settings.getMangaSources(includeHidden = false).asFlow() .flatMapMerge(concurrency) { source -> runCatching { - MangaRepository(source).getList( + mangaRepositoryFactory.create(source).getList( offset = 0, query = query, ) @@ -63,7 +66,7 @@ class MangaSearchRepository( SUGGESTION_PROJECTION, "${SearchManager.SUGGEST_COLUMN_QUERY} LIKE ?", arrayOf("%$query%"), - "date DESC" + "date DESC", )?.use { cursor -> val count = minOf(cursor.count, limit) if (count == 0) { @@ -126,7 +129,7 @@ class MangaSearchRepository( SUGGESTION_PROJECTION, null, arrayOfNulls(1), - null + null, )?.use { cursor -> cursor.count } ?: 0 } @@ -152,4 +155,4 @@ class MangaSearchRepository( return false } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/MangaListActivity.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/MangaListActivity.kt index cab89753f..8cef8c281 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/MangaListActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/MangaListActivity.kt @@ -5,11 +5,9 @@ import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import androidx.fragment.app.Fragment import androidx.fragment.app.commit import com.google.android.material.appbar.AppBarLayout -import org.koin.androidx.viewmodel.ext.android.getViewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags @@ -19,8 +17,8 @@ import org.koitharu.kotatsu.main.ui.AppBarOwner import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment -import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel +@AndroidEntryPoint class MangaListActivity : BaseActivity(), AppBarOwner { @@ -48,7 +46,7 @@ class MangaListActivity : RemoteListFragment.newInstance(source) } replace(R.id.container, fragment) - if (!tags.isNullOrEmpty()) { + if (!tags.isNullOrEmpty() && fragment is RemoteListFragment) { runOnCommit(ApplyFilterRunnable(fragment, tags)) } } @@ -59,21 +57,18 @@ class MangaListActivity : with(binding.toolbar) { updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) } } private class ApplyFilterRunnable( - private val fragment: Fragment, + private val fragment: RemoteListFragment, private val tags: Set, ) : Runnable { override fun run() { - val viewModel = fragment.getViewModel { - parametersOf(tags.first().source) - } - viewModel.applyFilter(tags) + fragment.viewModel.applyFilter(tags) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt index 857edd40f..1177c67c6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchActivity.kt @@ -3,13 +3,11 @@ package org.koitharu.kotatsu.search.ui import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.ViewGroup +import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.core.graphics.Insets -import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.commit -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivitySearchBinding @@ -19,7 +17,7 @@ import org.koitharu.kotatsu.utils.ext.showKeyboard class SearchActivity : BaseActivity(), SearchView.OnQueryTextListener { - private val searchSuggestionViewModel by viewModel() + private val searchSuggestionViewModel by viewModels() private lateinit var source: MangaSource override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchFragment.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchFragment.kt index ebfb1ace0..6e83fac82 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchFragment.kt @@ -2,20 +2,25 @@ package org.koitharu.kotatsu.search.ui import android.view.Menu import androidx.appcompat.view.ActionMode -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.serializableArgument import org.koitharu.kotatsu.utils.ext.stringArgument import org.koitharu.kotatsu.utils.ext.withArgs +@AndroidEntryPoint class SearchFragment : MangaListFragment() { - override val viewModel by viewModel { - parametersOf(source, query) + @Inject + lateinit var viewModelFactory: SearchViewModel.Factory + + override val viewModel by assistedViewModels { + viewModelFactory.create(source, query.orEmpty()) } private val query by stringArgument(ARG_QUERY) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt index 9615e84a6..5c4e257ea 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt @@ -1,6 +1,9 @@ package org.koitharu.kotatsu.search.ui import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -11,14 +14,17 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.* import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class SearchViewModel( - private val repository: MangaRepository, - private val query: String, - settings: AppSettings +class SearchViewModel @AssistedInject constructor( + @Assisted source: MangaSource, + @Assisted private val query: String, + repositoryFactory: MangaRepository.Factory, + settings: AppSettings, ) : MangaListViewModel(settings) { + private val repository = repositoryFactory.create(source) private val mangaList = MutableStateFlow?>(null) private val hasNextPage = MutableStateFlow(false) private val listError = MutableStateFlow(null) @@ -28,7 +34,7 @@ class SearchViewModel( mangaList, createListModeFlow(), listError, - hasNextPage + hasNextPage, ) { list, mode, error, hasNext -> when { list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true)) @@ -39,7 +45,7 @@ class SearchViewModel( textPrimary = R.string.nothing_found, textSecondary = R.string.text_search_holder_secondary, actionStringRes = 0, - ) + ), ) else -> { val result = ArrayList(list.size + 1) @@ -93,4 +99,10 @@ class SearchViewModel( } } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(source: MangaSource, query: String): SearchViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt index a6f2937ea..bdfb973ec 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt @@ -9,9 +9,9 @@ import android.view.View import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.list.ListSelectionController @@ -29,13 +29,23 @@ import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.search.ui.SearchActivity import org.koitharu.kotatsu.search.ui.multi.adapter.MultiSearchAdapter import org.koitharu.kotatsu.utils.ShareHelper +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations -class MultiSearchActivity : BaseActivity(), MangaListListener, +@AndroidEntryPoint +class MultiSearchActivity : + BaseActivity(), + MangaListListener, ListSelectionController.Callback { - private val viewModel by viewModel { - parametersOf(intent.getStringExtra(EXTRA_QUERY).orEmpty()) + @Inject + lateinit var viewModelFactory: MultiSearchViewModel.Factory + + @Inject + lateinit var coil: ImageLoader + + private val viewModel by assistedViewModels { + viewModelFactory.create(intent.getStringExtra(EXTRA_QUERY).orEmpty()) } private lateinit var adapter: MultiSearchAdapter private lateinit var selectionController: ListSelectionController @@ -49,7 +59,7 @@ class MultiSearchActivity : BaseActivity(), MangaLis startActivity(SearchActivity.newIntent(view.context, item.source, viewModel.query.value)) } } - val sizeResolver = ItemSizeResolver(resources, get()) + val sizeResolver = ItemSizeResolver(resources, settings) val selectionDecoration = MangaSelectionDecoration(this) selectionController = ListSelectionController( activity = this, @@ -59,7 +69,7 @@ class MultiSearchActivity : BaseActivity(), MangaLis ) adapter = MultiSearchAdapter( lifecycleOwner = this, - coil = get(), + coil = coil, listener = this, itemClickListener = itemCLickListener, sizeResolver = sizeResolver, diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchViewModel.kt index b2ababf6d..e66c741c0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchViewModel.kt @@ -3,6 +3,9 @@ package org.koitharu.kotatsu.search.ui.multi import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -21,9 +24,10 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug private const val MAX_PARALLELISM = 4 private const val MIN_HAS_MORE_ITEMS = 8 -class MultiSearchViewModel( - initialQuery: String, +class MultiSearchViewModel @AssistedInject constructor( + @Assisted initialQuery: String, private val settings: AppSettings, + private val mangaRepositoryFactory: MangaRepository.Factory, ) : BaseViewModel() { private var searchJob: Job? = null @@ -48,7 +52,7 @@ class MultiSearchViewModel( textSecondary = R.string.text_search_holder_secondary, actionStringRes = 0, ) - } + }, ) loading -> list + LoadingFooter else -> list @@ -95,7 +99,7 @@ class MultiSearchViewModel( val deferredList = sources.map { source -> async(dispatcher) { runCatching { - val list = MangaRepository(source).getList(offset = 0, query = q) + val list = mangaRepositoryFactory.create(source).getList(offset = 0, query = q) .toUi(ListMode.GRID) if (list.isNotEmpty()) { MultiSearchListModel(source, list.size > MIN_HAS_MORE_ITEMS, list) @@ -127,4 +131,10 @@ class MultiSearchViewModel( } } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(initialQuery: String): MultiSearchViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt index 15c375382..59e95bdbb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt @@ -6,9 +6,10 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.ItemTouchHelper -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import coil.ImageLoader +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.databinding.FragmentSearchSuggestionBinding @@ -19,7 +20,10 @@ class SearchSuggestionFragment : BaseFragment(), SearchSuggestionItemCallback.SuggestionItemListener { - private val viewModel by sharedViewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by activityViewModels() override fun onInflateView( inflater: LayoutInflater, @@ -29,7 +33,7 @@ class SearchSuggestionFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val adapter = SearchSuggestionAdapter( - coil = get(), + coil = coil, lifecycleOwner = viewLifecycleOwner, listener = requireActivity() as SearchSuggestionListener, ) @@ -49,7 +53,7 @@ class SearchSuggestionFragment : top = extraPadding, right = insets.right, left = insets.left, - bottom = insets.bottom + bottom = insets.bottom, ) } @@ -61,4 +65,4 @@ class SearchSuggestionFragment : fun newInstance() = SearchSuggestionFragment() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt index ed9ec5baf..377b2c17c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt @@ -2,6 +2,8 @@ package org.koitharu.kotatsu.search.ui.suggestion import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -19,7 +21,8 @@ private const val MAX_QUERY_ITEMS = 16 private const val MAX_TAGS_ITEMS = 8 private const val MAX_SOURCES_ITEMS = 6 -class SearchSuggestionViewModel( +@HiltViewModel +class SearchSuggestionViewModel @Inject constructor( private val repository: MangaSearchRepository, private val settings: AppSettings, ) : BaseViewModel() { @@ -117,4 +120,4 @@ class SearchSuggestionViewModel( isChecked = false, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt b/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt index 1308de583..7d8cccdd8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt @@ -17,7 +17,6 @@ import java.security.cert.X509Certificate import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.koin.android.ext.android.get import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.github.AppUpdateRepository @@ -31,8 +30,8 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug @Deprecated("") class AppUpdateChecker(private val activity: ComponentActivity) { - private val settings = activity.get() - private val repo = activity.get() + private val settings: AppSettings = TODO() + private val repo: AppUpdateRepository = TODO() suspend fun checkIfNeeded(): Boolean? = if ( settings.isUpdateCheckingEnabled && diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt index 50bb254df..e71bb1576 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt @@ -10,7 +10,9 @@ import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.TwoStatePreference import com.google.android.material.color.DynamicColors -import org.koin.android.ext.android.get +import dagger.hilt.android.AndroidEntryPoint +import java.util.* +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.base.ui.util.ActivityRecreationHandle @@ -20,12 +22,15 @@ import org.koitharu.kotatsu.parsers.util.names import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity import org.koitharu.kotatsu.settings.utils.SliderPreference import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat -import java.util.* +@AndroidEntryPoint class AppearanceSettingsFragment : BasePreferenceFragment(R.string.appearance), SharedPreferences.OnSharedPreferenceChangeListener { + @Inject + lateinit var activityRecreationHandle: ActivityRecreationHandle + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_appearance) findPreference(AppSettings.KEY_GRID_SIZE)?.run { @@ -105,7 +110,7 @@ class AppearanceSettingsFragment : private fun postRestart() { view?.postDelayed(400) { - get().recreateAll() + activityRecreationHandle.recreateAll() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt index 9d70660c7..83a5f231a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt @@ -8,10 +8,12 @@ import android.view.View import androidx.preference.ListPreference import androidx.preference.Preference import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import java.io.File +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog @@ -24,14 +26,15 @@ import org.koitharu.kotatsu.sync.ui.SyncSettingsIntent import org.koitharu.kotatsu.utils.ext.getStorageName import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat import org.koitharu.kotatsu.utils.ext.viewLifecycleScope -import java.io.File +@AndroidEntryPoint class ContentSettingsFragment : BasePreferenceFragment(R.string.content), SharedPreferences.OnSharedPreferenceChangeListener, StorageSelectDialog.OnStorageSelectListener { - private val storageManager by inject() + @Inject + lateinit var storageManager: LocalStorageManager override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_content) @@ -58,7 +61,7 @@ class ContentSettingsFragment : super.onViewCreated(view, savedInstanceState) findPreference(AppSettings.KEY_LOCAL_STORAGE)?.bindStorageName() findPreference(AppSettings.KEY_SUGGESTIONS)?.setSummary( - if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled + if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled, ) bindRemoteSourcesSummary() settings.subscribe(this) @@ -81,7 +84,7 @@ class ContentSettingsFragment : } AppSettings.KEY_SUGGESTIONS -> { findPreference(AppSettings.KEY_SUGGESTIONS)?.setSummary( - if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled + if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled, ) } AppSettings.KEY_SOURCES_HIDDEN -> { diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt index c4c5f46ba..038e07edf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt @@ -7,9 +7,9 @@ import android.view.View import androidx.preference.Preference import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.get -import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.network.AndroidCookieJar @@ -23,12 +23,23 @@ import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +@AndroidEntryPoint class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cache) { - private val trackerRepo by inject(mode = LazyThreadSafetyMode.NONE) - private val searchRepository by inject(mode = LazyThreadSafetyMode.NONE) - private val storageManager by inject(mode = LazyThreadSafetyMode.NONE) - private val shikimoriRepository by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var trackerRepo: TrackingRepository + + @Inject + lateinit var searchRepository: MangaSearchRepository + + @Inject + lateinit var storageManager: LocalStorageManager + + @Inject + lateinit var shikimoriRepository: ShikimoriRepository + + @Inject + lateinit var cookieJar: AndroidCookieJar override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_history) @@ -85,7 +96,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach Snackbar.make( view ?: return@launch, R.string.updates_feed_cleared, - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ).show() } true @@ -136,7 +147,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach Snackbar.make( view ?: return@launch, R.string.search_history_cleared, - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ).show() } }.show() @@ -149,12 +160,11 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.clear) { _, _ -> viewLifecycleScope.launch { - val cookieJar = get() cookieJar.clear() Snackbar.make( listView ?: return@launch, R.string.cookies_cleared, - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ).show() } }.show() @@ -177,4 +187,4 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach Snackbar.make(listView, it.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/NotificationSettingsLegacyFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/NotificationSettingsLegacyFragment.kt index ee15c796a..dedac9988 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/NotificationSettingsLegacyFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/NotificationSettingsLegacyFragment.kt @@ -1,12 +1,10 @@ package org.koitharu.kotatsu.settings -import android.content.Context import android.content.SharedPreferences import android.media.RingtoneManager import android.os.Bundle import android.view.View import androidx.preference.Preference -import org.koin.android.ext.android.get import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.prefs.AppSettings @@ -17,7 +15,7 @@ class NotificationSettingsLegacyFragment : SharedPreferences.OnSharedPreferenceChangeListener { private val ringtonePickContract = registerForActivityResult( - RingtonePickContract(get().getString(R.string.notification_sound)) + RingtonePickContract(R.string.notification_sound), ) { uri -> settings.notificationSound = uri ?: return@registerForActivityResult findPreference(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run { @@ -66,4 +64,4 @@ class NotificationSettingsLegacyFragment : findPreference(AppSettings.KEY_NOTIFICATIONS_INFO) ?.isVisible = !settings.isTrackerNotificationsEnabled } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt index b3695389d..93e2eb6a8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -16,6 +16,7 @@ import androidx.fragment.app.commit import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.appbar.AppBarLayout +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity @@ -27,6 +28,7 @@ import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.utils.ext.isScrolledToTop +@AndroidEntryPoint class SettingsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, @@ -88,7 +90,7 @@ class SettingsActivity : override fun onPreferenceStartFragment( caller: PreferenceFragmentCompat, - pref: Preference + pref: Preference, ): Boolean { val fm = supportFragmentManager val fragment = fm.fragmentFactory.instantiate(classLoader, pref.fragment ?: return false) @@ -126,7 +128,7 @@ class SettingsActivity : ACTION_SHIKIMORI -> ShikimoriSettingsFragment() ACTION_TRACKER -> TrackerSettingsFragment() ACTION_SOURCE -> SourceSettingsFragment.newInstance( - intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL + intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL, ) ACTION_MANAGE_SOURCES -> SourcesSettingsFragment() else -> SettingsHeadersFragment() @@ -185,4 +187,4 @@ class SettingsActivity : .setAction(ACTION_SOURCE) .putExtra(EXTRA_SOURCE, source) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt deleted file mode 100644 index 35371fbd7..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.koitharu.kotatsu.settings - -import android.net.Uri -import android.os.Build -import androidx.room.InvalidationTracker -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.core.backup.BackupRepository -import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.settings.backup.BackupObserver -import org.koitharu.kotatsu.settings.backup.BackupViewModel -import org.koitharu.kotatsu.settings.backup.RestoreViewModel -import org.koitharu.kotatsu.settings.newsources.NewSourcesViewModel -import org.koitharu.kotatsu.settings.onboard.OnboardViewModel -import org.koitharu.kotatsu.settings.protect.ProtectSetupViewModel -import org.koitharu.kotatsu.settings.sources.SourcesSettingsViewModel -import org.koitharu.kotatsu.settings.tools.ToolsViewModel - -val settingsModule - get() = module { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - single { BackupObserver(androidContext()) } - } - - factory { BackupRepository(get()) } - single(createdAtStart = true) { AppSettings(androidContext()) } - - viewModel { BackupViewModel(get(), androidContext()) } - viewModel { params -> - RestoreViewModel(params.getOrNull(Uri::class), get(), androidContext()) - } - viewModel { ProtectSetupViewModel(get()) } - viewModel { OnboardViewModel(get()) } - viewModel { SourcesSettingsViewModel(get()) } - viewModel { NewSourcesViewModel(get()) } - viewModel { ToolsViewModel(get(), get(), get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt index 3db0bb810..3436ca259 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt @@ -4,6 +4,8 @@ import android.os.Bundle import android.view.View import androidx.preference.Preference import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch @@ -18,8 +20,12 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity import org.koitharu.kotatsu.utils.ext.* +@AndroidEntryPoint class SourceSettingsFragment : BasePreferenceFragment(0) { + @Inject + lateinit var mangaRepositoryFactory: MangaRepository.Factory + private val source by serializableArgument(EXTRA_SOURCE) private var repository: RemoteMangaRepository? = null private val exceptionResolver = ExceptionResolver(this) @@ -31,7 +37,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.sharedPreferencesName = source.name - val repo = MangaRepository(source) as? RemoteMangaRepository ?: return + val repo = mangaRepositoryFactory.create(source) as? RemoteMangaRepository ?: return repository = repo addPreferencesFromResource(R.xml.pref_source) addPreferencesFromRepository(repo) @@ -89,7 +95,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { } } - private fun resolveError(error: Throwable): Unit { + private fun resolveError(error: Throwable) { viewLifecycleScope.launch { if (exceptionResolver.resolve(error)) { val pref = findPreference(KEY_AUTH) ?: return@launch @@ -108,4 +114,4 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { putSerializable(EXTRA_SOURCE, source) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt index 46aced8a0..0ba19a211 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt @@ -3,9 +3,9 @@ package org.koitharu.kotatsu.settings import android.content.SharedPreferences import android.os.Bundle import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.get -import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.prefs.AppSettings @@ -14,10 +14,16 @@ import org.koitharu.kotatsu.settings.utils.TagsAutoCompleteProvider import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker -class SuggestionsSettingsFragment : BasePreferenceFragment(R.string.suggestions), +@AndroidEntryPoint +class SuggestionsSettingsFragment : + BasePreferenceFragment(R.string.suggestions), SharedPreferences.OnSharedPreferenceChangeListener { - private val repository by inject(mode = LazyThreadSafetyMode.NONE) + @Inject + lateinit var repository: SuggestionRepository + + @Inject + lateinit var tagsCompletionProvider: TagsAutoCompleteProvider override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -28,7 +34,7 @@ class SuggestionsSettingsFragment : BasePreferenceFragment(R.string.suggestions) addPreferencesFromResource(R.xml.pref_suggestions) findPreference(AppSettings.KEY_SUGGESTIONS_EXCLUDE_TAGS)?.run { - autoCompleteProvider = TagsAutoCompleteProvider(get()) + autoCompleteProvider = tagsCompletionProvider summaryProvider = MultiAutoCompleteTextViewPreference.SimpleSummaryProvider(summary) } } @@ -51,4 +57,4 @@ class SuggestionsSettingsFragment : BasePreferenceFragment(R.string.suggestions) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt index 7f85fcc2e..58a0308f9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt @@ -18,8 +18,9 @@ import androidx.core.text.inSpans import androidx.preference.MultiSelectListPreference import androidx.preference.Preference import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch -import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.prefs.AppSettings @@ -31,12 +32,16 @@ import org.koitharu.kotatsu.utils.ext.viewLifecycleScope private const val KEY_IGNORE_DOZE = "ignore_dose" +@AndroidEntryPoint class TrackerSettingsFragment : BasePreferenceFragment(R.string.check_for_new_chapters), SharedPreferences.OnSharedPreferenceChangeListener { - private val repository by inject() - private val channels by inject() + @Inject + lateinit var repository: TrackingRepository + + @Inject + lateinit var channels: TrackerNotificationChannels override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_tracker) @@ -79,7 +84,8 @@ class TrackerSettingsFragment : when (key) { AppSettings.KEY_TRACKER_NOTIFICATIONS -> updateNotificationsSummary() AppSettings.KEY_TRACK_SOURCES, - AppSettings.KEY_TRACKER_ENABLED -> updateCategoriesEnabled() + AppSettings.KEY_TRACKER_ENABLED, + -> updateCategoriesEnabled() } } @@ -121,7 +127,7 @@ class TrackerSettingsFragment : channels.areNotificationsDisabled -> R.string.disabled channels.isNotificationGroupEnabled() -> R.string.show_notification_new_chapters_on else -> R.string.show_notification_new_chapters_off - } + }, ) } @@ -167,4 +173,4 @@ class TrackerSettingsFragment : val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager return !powerManager.isIgnoringBatteryOptimizations(packageName) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt index b309a588b..df52529b7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt @@ -8,23 +8,23 @@ import android.view.ViewGroup import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.isVisible +import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.androidx.viewmodel.ext.android.viewModel +import java.io.File +import java.io.FileOutputStream import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.databinding.DialogProgressBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.progress.Progress -import java.io.File -import java.io.FileOutputStream class BackupDialogFragment : AlertDialogFragment() { - private val viewModel by viewModel() + private val viewModel by viewModels() private var backup: File? = null private val saveFileContract = registerForActivityResult( - ActivityResultContracts.CreateDocument("*/*") + ActivityResultContracts.CreateDocument("*/*"), ) { uri -> val file = backup if (uri != null && file != null) { @@ -97,4 +97,4 @@ class BackupDialogFragment : AlertDialogFragment() { const val TAG = "BackupDialogFragment" } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupObserver.kt b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupObserver.kt index 807e63e56..1e8c5d21b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupObserver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupObserver.kt @@ -2,16 +2,17 @@ package org.koitharu.kotatsu.settings.backup import android.app.backup.BackupManager import android.content.Context -import android.os.Build -import androidx.annotation.RequiresApi import androidx.room.InvalidationTracker +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES import org.koitharu.kotatsu.core.db.TABLE_HISTORY -@RequiresApi(Build.VERSION_CODES.M) -class BackupObserver( - context: Context, +@Singleton +class BackupObserver @Inject constructor( + @ApplicationContext context: Context, ) : InvalidationTracker.Observer(arrayOf(TABLE_HISTORY, TABLE_FAVOURITES, TABLE_FAVOURITE_CATEGORIES)) { private val backupManager = BackupManager(context) @@ -19,4 +20,4 @@ class BackupObserver( override fun onInvalidated(tables: MutableSet) { backupManager.dataChanged() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupViewModel.kt index 2532dc8d2..1f822aa82 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/backup/BackupViewModel.kt @@ -2,16 +2,20 @@ package org.koitharu.kotatsu.settings.backup import android.content.Context import androidx.lifecycle.MutableLiveData +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.backup.BackupRepository import org.koitharu.kotatsu.core.backup.BackupZipOutput import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.progress.Progress -import java.io.File -class BackupViewModel( +@HiltViewModel +class BackupViewModel @Inject constructor( private val repository: BackupRepository, - context: Context + @ApplicationContext context: Context, ) : BaseViewModel() { val progress = MutableLiveData(null) @@ -40,4 +44,4 @@ class BackupViewModel( onBackupDone.call(file) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt index 9ccb5e1b8..5af836d4b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt @@ -7,28 +7,33 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.core.backup.CompositeResult import org.koitharu.kotatsu.databinding.DialogProgressBinding +import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.toUriOrNull import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.progress.Progress +@AndroidEntryPoint class RestoreDialogFragment : AlertDialogFragment() { + @Inject + lateinit var viewModelFactory: RestoreViewModel.Factory + + private val viewModel by assistedViewModels { + viewModelFactory.create(arguments?.getString(ARG_FILE)?.toUriOrNull()) + } + override fun onInflateView( inflater: LayoutInflater, container: ViewGroup?, ) = DialogProgressBinding.inflate(inflater, container, false) - private val viewModel by viewModel { - parametersOf(arguments?.getString(ARG_FILE)?.toUriOrNull()) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.textViewTitle.setText(R.string.restore_backup) @@ -72,7 +77,7 @@ class RestoreDialogFragment : AlertDialogFragment() { .setMessage( result.failures.map { it.getDisplayMessage(resources) - }.distinct().joinToString("\n") + }.distinct().joinToString("\n"), ) else -> builder.setTitle(R.string.data_restored) .setMessage(R.string.data_restored_with_errors) @@ -91,4 +96,4 @@ class RestoreDialogFragment : AlertDialogFragment() { putString(ARG_FILE, uri.toString()) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt index f63522140..71bb4ea3a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt @@ -3,6 +3,12 @@ package org.koitharu.kotatsu.settings.backup import android.content.Context import android.net.Uri import androidx.lifecycle.MutableLiveData +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import java.io.FileNotFoundException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -12,13 +18,11 @@ import org.koitharu.kotatsu.core.backup.BackupZipInput import org.koitharu.kotatsu.core.backup.CompositeResult import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.progress.Progress -import java.io.File -import java.io.FileNotFoundException -class RestoreViewModel( - uri: Uri?, +class RestoreViewModel @AssistedInject constructor( + @Assisted uri: Uri?, private val repository: BackupRepository, - context: Context + @ApplicationContext context: Context, ) : BaseViewModel() { val progress = MutableLiveData(null) @@ -60,4 +64,10 @@ class RestoreViewModel( } } } -} \ No newline at end of file + + @AssistedFactory + interface Factory { + + fun create(uri: Uri?): RestoreViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt index 9718e5306..935a6a6bd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt @@ -6,10 +6,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView +import coil.ImageLoader import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.databinding.DialogOnboardBinding @@ -22,7 +23,10 @@ class NewSourcesDialogFragment : SourceConfigListener, DialogInterface.OnClickListener { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by viewModels() override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogOnboardBinding { return DialogOnboardBinding.inflate(inflater, container, false) @@ -30,7 +34,7 @@ class NewSourcesDialogFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val adapter = SourceConfigAdapter(this, get(), viewLifecycleOwner) + val adapter = SourceConfigAdapter(this, coil, viewLifecycleOwner) binding.recyclerView.adapter = adapter binding.textViewTitle.setText(R.string.new_sources_text) @@ -65,4 +69,4 @@ class NewSourcesDialogFragment : fun show(fm: FragmentManager) = NewSourcesDialogFragment().show(fm, TAG) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesViewModel.kt index 06567642b..cf607eabd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/newsources/NewSourcesViewModel.kt @@ -2,13 +2,16 @@ package org.koitharu.kotatsu.settings.newsources import androidx.core.os.LocaleListCompat import androidx.lifecycle.MutableLiveData +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.getLocaleTitle import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import org.koitharu.kotatsu.utils.ext.mapToSet -class NewSourcesViewModel( +@HiltViewModel +class NewSourcesViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { @@ -44,4 +47,4 @@ class NewSourcesViewModel( ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardDialogFragment.kt index 932306fe3..f20fc60d9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardDialogFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardDialogFragment.kt @@ -6,8 +6,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager +import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -22,7 +22,7 @@ class OnboardDialogFragment : OnListItemClickListener, DialogInterface.OnClickListener { - private val viewModel by viewModel() + private val viewModel by viewModels() private var isWelcome: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { @@ -83,4 +83,4 @@ class OnboardDialogFragment : }.showAllowStateLoss(fm, TAG) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardViewModel.kt index 2f1495243..30bda62a0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/onboard/OnboardViewModel.kt @@ -3,6 +3,9 @@ package org.koitharu.kotatsu.settings.onboard import androidx.collection.ArraySet import androidx.core.os.LocaleListCompat import androidx.lifecycle.MutableLiveData +import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.* +import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.model.MangaSource @@ -11,9 +14,9 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.settings.onboard.model.SourceLocale import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.mapToSet -import java.util.* -class OnboardViewModel( +@HiltViewModel +class OnboardViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { @@ -66,7 +69,7 @@ class OnboardViewModel( SourceLocale( key = key, title = locale?.getDisplayLanguage(locale)?.toTitleCase(locale), - isChecked = key in selectedLocales + isChecked = key in selectedLocales, ) }.sortedWith(SourceLocaleComparator()) } @@ -92,4 +95,4 @@ class OnboardViewModel( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt index f88a8dad9..1bbbf9951 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt @@ -11,20 +11,24 @@ import android.view.WindowManager import android.view.inputmethod.EditorInfo import android.widget.CompoundButton import android.widget.TextView +import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.isGone import androidx.core.view.isVisible -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding private const val MIN_PASSWORD_LENGTH = 4 -class ProtectSetupActivity : BaseActivity(), TextWatcher, - View.OnClickListener, TextView.OnEditorActionListener, CompoundButton.OnCheckedChangeListener { +class ProtectSetupActivity : + BaseActivity(), + TextWatcher, + View.OnClickListener, + TextView.OnEditorActionListener, + CompoundButton.OnCheckedChangeListener { - private val viewModel by viewModel() + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -56,7 +60,7 @@ class ProtectSetupActivity : BaseActivity(), TextWa basePadding + insets.left, basePadding + insets.top, basePadding + insets.right, - basePadding + insets.bottom + basePadding + insets.bottom, ) } @@ -64,7 +68,7 @@ class ProtectSetupActivity : BaseActivity(), TextWa when (v.id) { R.id.button_cancel -> finish() R.id.button_next -> viewModel.onNextClick( - password = binding.editPassword.text?.toString() ?: return + password = binding.editPassword.text?.toString() ?: return, ) } } @@ -110,4 +114,4 @@ class ProtectSetupActivity : BaseActivity(), TextWa return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt index 0018a61a1..b07860bbe 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.settings.protect import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.base.ui.BaseViewModel @@ -9,8 +11,9 @@ import org.koitharu.kotatsu.parsers.util.md5 import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct -class ProtectSetupViewModel( - private val settings: AppSettings +@HiltViewModel +class ProtectSetupViewModel @Inject constructor( + private val settings: AppSettings, ) : BaseViewModel() { private val firstPassword = MutableStateFlow(null) @@ -42,4 +45,4 @@ class ProtectSetupViewModel( fun setBiometricEnabled(isEnabled: Boolean) { settings.isBiometricProtectionEnabled = isEnabled } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt index 10453c27e..007db7a79 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt @@ -6,10 +6,12 @@ import androidx.appcompat.widget.SearchView import androidx.core.graphics.Insets import androidx.core.view.MenuProvider import androidx.core.view.updatePadding +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner @@ -23,20 +25,24 @@ import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import org.koitharu.kotatsu.utils.ext.addMenuProvider +@AndroidEntryPoint class SourcesSettingsFragment : BaseFragment(), SourceConfigListener, RecyclerViewOwner { + @Inject + lateinit var coil: ImageLoader + private var reorderHelper: ItemTouchHelper? = null - private val viewModel by viewModel() + private val viewModel by viewModels() override val recyclerView: RecyclerView get() = binding.recyclerView override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentSettingsSourcesBinding.inflate(inflater, container, false) override fun onResume() { @@ -46,7 +52,7 @@ class SourcesSettingsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val sourcesAdapter = SourceConfigAdapter(this, get(), viewLifecycleOwner) + val sourcesAdapter = SourceConfigAdapter(this, coil, viewLifecycleOwner) with(binding.recyclerView) { setHasFixedSize(true) adapter = sourcesAdapter @@ -69,7 +75,7 @@ class SourcesSettingsFragment : binding.recyclerView.updatePadding( bottom = insets.bottom, left = insets.left, - right = insets.right + right = insets.right, ) } @@ -159,4 +165,4 @@ class SourcesSettingsFragment : override fun isLongPressDragEnabled() = false } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt index 3a5340fe8..2ff8a9ff8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt @@ -2,6 +2,9 @@ package org.koitharu.kotatsu.settings.sources import androidx.core.os.LocaleListCompat import androidx.lifecycle.MutableLiveData +import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.* +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.getLocaleTitle @@ -12,11 +15,11 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.move -import java.util.* private const val KEY_ENABLED = "!" -class SourcesSettingsViewModel( +@HiltViewModel +class SourcesSettingsViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { @@ -171,4 +174,4 @@ class SourcesSettingsViewModel( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index 41f4afa84..4cac8671f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -12,6 +12,8 @@ import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding import com.google.android.material.R as materialR +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.browser.BrowserCallback @@ -25,8 +27,12 @@ import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.TaggedActivityResult +@AndroidEntryPoint class SourceAuthActivity : BaseActivity(), BrowserCallback { + @Inject + lateinit var mangaRepositoryFactory: MangaRepository.Factory + private lateinit var authProvider: MangaParserAuthProvider @SuppressLint("SetJavaScriptEnabled") @@ -38,11 +44,11 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba finishAfterTransition() return } - authProvider = (MangaRepository(source) as? RemoteMangaRepository)?.getAuthProvider() ?: run { + authProvider = (mangaRepositoryFactory.create(source) as? RemoteMangaRepository)?.getAuthProvider() ?: run { Toast.makeText( this, getString(R.string.auth_not_supported_by, source.title), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() finishAfterTransition() return @@ -63,7 +69,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba val url = authProvider.authUrl onTitleChanged( source.title, - getString(R.string.loading_) + getString(R.string.loading_), ) binding.webView.loadUrl(url) } @@ -150,4 +156,4 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba .putExtra(EXTRA_SOURCE, source) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsFragment.kt index 0fd64bb6a..a84fcf7ac 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsFragment.kt @@ -15,9 +15,10 @@ import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.core.widget.TextViewCompat +import androidx.fragment.app.viewModels import com.google.android.material.R as materialR import com.google.android.material.color.MaterialColors -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.widgets.SegmentedBarView @@ -29,12 +30,13 @@ import org.koitharu.kotatsu.settings.tools.model.StorageUsage import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.ext.getThemeColor +@AndroidEntryPoint class ToolsFragment : BaseFragment(), CompoundButton.OnCheckedChangeListener, View.OnClickListener { - private val viewModel by viewModel() + private val viewModel by viewModels() override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): FragmentToolsBinding { return FragmentToolsBinding.inflate(inflater, container, false) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsViewModel.kt index 06b581304..e2764ef5d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/tools/ToolsViewModel.kt @@ -4,6 +4,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.github.AppUpdateRepository @@ -13,7 +15,8 @@ import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.settings.tools.model.StorageUsage -class ToolsViewModel( +@HiltViewModel +class ToolsViewModel @Inject constructor( private val storageManager: LocalStorageManager, private val appUpdateRepository: AppUpdateRepository, private val settings: AppSettings, diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt index 3920cb32c..dee96c004 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/RingtonePickContract.kt @@ -6,23 +6,24 @@ import android.media.RingtoneManager import android.net.Uri import android.provider.Settings import androidx.activity.result.contract.ActivityResultContract +import androidx.annotation.StringRes -class RingtonePickContract(private val title: String?) : ActivityResultContract() { +class RingtonePickContract(@StringRes private val titleResId: Int) : ActivityResultContract() { override fun createIntent(context: Context, input: Uri?): Intent { val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER) intent.putExtra( RingtoneManager.EXTRA_RINGTONE_TYPE, - RingtoneManager.TYPE_NOTIFICATION + RingtoneManager.TYPE_NOTIFICATION, ) intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true) intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true) intent.putExtra( RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, - Settings.System.DEFAULT_NOTIFICATION_URI + Settings.System.DEFAULT_NOTIFICATION_URI, ) - if (title != null) { - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, title) + if (titleResId != 0) { + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(titleResId)) } intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, input) return intent @@ -31,4 +32,4 @@ class RingtonePickContract(private val title: String?) : ActivityResultContract< override fun parseResult(resultCode: Int, intent: Intent?): Uri? { return intent?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/TagsAutoCompleteProvider.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/TagsAutoCompleteProvider.kt index 53998e48b..27de0056d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/TagsAutoCompleteProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/TagsAutoCompleteProvider.kt @@ -1,8 +1,9 @@ package org.koitharu.kotatsu.settings.utils +import javax.inject.Inject import org.koitharu.kotatsu.core.db.MangaDatabase -class TagsAutoCompleteProvider( +class TagsAutoCompleteProvider @Inject constructor( private val db: MangaDatabase, ) : MultiAutoCompleteTextViewPreference.AutoCompleteProvider { @@ -20,4 +21,4 @@ class TagsAutoCompleteProvider( } return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/SuggestionsModule.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/SuggestionsModule.kt deleted file mode 100644 index df0a2c870..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/SuggestionsModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.koitharu.kotatsu.suggestions - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository -import org.koitharu.kotatsu.suggestions.ui.SuggestionsViewModel - -val suggestionsModule - get() = module { - - factory { SuggestionRepository(get()) } - - viewModel { SuggestionsViewModel(get(), get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt index 398a0a0f0..f0afaf429 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/domain/SuggestionRepository.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.suggestions.domain import androidx.room.withTransaction +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.toEntities @@ -11,7 +12,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.suggestions.data.SuggestionEntity import org.koitharu.kotatsu.utils.ext.mapItems -class SuggestionRepository( +class SuggestionRepository @Inject constructor( private val db: MangaDatabase, ) { @@ -41,9 +42,9 @@ class SuggestionRepository( mangaId = manga.id, relevance = relevance, createdAt = System.currentTimeMillis(), - ) + ), ) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt index 2c03fca6f..a1c8ad796 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt @@ -7,8 +7,8 @@ import android.view.MenuItem import android.view.View import androidx.appcompat.view.ActionMode import androidx.core.view.MenuProvider +import androidx.fragment.app.viewModels import com.google.android.material.snackbar.Snackbar -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.list.ui.MangaListFragment @@ -17,7 +17,7 @@ import org.koitharu.kotatsu.utils.ext.addMenuProvider class SuggestionsFragment : MangaListFragment() { - override val viewModel by viewModel() + override val viewModel by viewModels() override val isSwipeRefreshEnabled = false override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt index 40ec8487c..d8d19d243 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.suggestions.ui import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -16,14 +18,15 @@ import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.onFirst -class SuggestionsViewModel( +@HiltViewModel +class SuggestionsViewModel @Inject constructor( repository: SuggestionRepository, settings: AppSettings, ) : MangaListViewModel(settings) { override val content = combine( repository.observeAll(), - createListModeFlow() + createListModeFlow(), ) { list, mode -> when { list.isEmpty() -> listOf( @@ -32,7 +35,7 @@ class SuggestionsViewModel( textPrimary = R.string.nothing_found, textSecondary = R.string.text_suggestion_holder, actionStringRes = 0, - ) + ), ) else -> list.toUi(mode) } @@ -44,10 +47,10 @@ class SuggestionsViewModel( it.toErrorState(canRetry = false) }.asLiveDataDistinct( viewModelScope.coroutineContext + Dispatchers.Default, - listOf(LoadingState) + listOf(LoadingState), ) override fun onRefresh() = Unit override fun onRetry() = Unit -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt index 7433e689b..5f94cd5c1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt @@ -7,13 +7,16 @@ import android.os.Build import androidx.annotation.FloatRange import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat +import androidx.hilt.work.HiltWorker import androidx.work.* +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import java.util.concurrent.TimeUnit +import kotlin.math.pow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings @@ -24,15 +27,16 @@ import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository import org.koitharu.kotatsu.utils.ext.asArrayList import org.koitharu.kotatsu.utils.ext.trySetForeground -import java.util.concurrent.TimeUnit -import kotlin.math.pow -class SuggestionsWorker(appContext: Context, params: WorkerParameters) : - CoroutineWorker(appContext, params), KoinComponent { - - private val suggestionRepository by inject() - private val historyRepository by inject() - private val appSettings by inject() +@HiltWorker +class SuggestionsWorker @AssistedInject constructor( + @Assisted appContext: Context, + @Assisted params: WorkerParameters, + private val suggestionRepository: SuggestionRepository, + private val historyRepository: HistoryRepository, + private val appSettings: AppSettings, + private val mangaRepositoryFactory: MangaRepository.Factory, +) : CoroutineWorker(appContext, params) { override suspend fun doWork(): Result { val count = doWorkImpl() @@ -47,7 +51,7 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) : val channel = NotificationChannel( WORKER_CHANNEL_ID, title, - NotificationManager.IMPORTANCE_LOW + NotificationManager.IMPORTANCE_LOW, ) channel.setShowBadge(false) channel.enableVibration(false) @@ -90,7 +94,7 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) : val dispatcher = Dispatchers.Default.limitedParallelism(MAX_PARALLELISM) val rawResults = coroutineScope { tagsBySources.flatMap { (source, tags) -> - val repo = MangaRepository(source) + val repo = mangaRepositoryFactory.create(source) tags.map { tag -> async(dispatcher) { repo.getList( @@ -118,7 +122,7 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) : }.map { manga -> MangaSuggestion( manga = manga, - relevance = computeRelevance(manga.tags, allTags) + relevance = computeRelevance(manga.tags, allTags), ) }.sortedBy { it.relevance }.take(LIMIT) suggestionRepository.replace(suggestions) @@ -173,4 +177,4 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) : .enqueue(request) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt b/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt deleted file mode 100644 index ba0d9f2a9..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.sync - -import androidx.room.InvalidationTracker -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koitharu.kotatsu.sync.data.SyncAuthApi -import org.koitharu.kotatsu.sync.domain.SyncController -import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel - -val syncModule - get() = module { - - single { SyncController(androidContext()) } bind InvalidationTracker.Observer::class - - factory { SyncAuthApi(androidContext(), get()) } - - viewModel { SyncAuthViewModel(get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt b/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt index 1d56dc91a..22e8d9d78 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.sync.data import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject import okhttp3.OkHttpClient import okhttp3.Request import org.json.JSONObject @@ -11,8 +13,8 @@ import org.koitharu.kotatsu.parsers.util.parseJson import org.koitharu.kotatsu.parsers.util.removeSurrounding import org.koitharu.kotatsu.utils.ext.toRequestBody -class SyncAuthApi( - context: Context, +class SyncAuthApi @Inject constructor( + @ApplicationContext context: Context, private val okHttpClient: OkHttpClient, ) { @@ -20,7 +22,7 @@ class SyncAuthApi( suspend fun authenticate(email: String, password: String): String { val body = JSONObject( - mapOf("email" to email, "password" to password) + mapOf("email" to email, "password" to password), ).toRequestBody() val request = Request.Builder() .url("$baseUrl/auth") diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncController.kt b/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncController.kt index 2924b8480..9dff00923 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncController.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncController.kt @@ -8,6 +8,10 @@ import android.os.Bundle import android.util.ArrayMap import androidx.room.InvalidationTracker import androidx.room.withTransaction +import dagger.hilt.android.qualifiers.ApplicationContext +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -18,10 +22,10 @@ import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES import org.koitharu.kotatsu.core.db.TABLE_HISTORY import org.koitharu.kotatsu.utils.ext.processLifecycleScope -import java.util.concurrent.TimeUnit -class SyncController( - context: Context, +@Singleton +class SyncController @Inject constructor( + @ApplicationContext context: Context, ) : InvalidationTracker.Observer(arrayOf(TABLE_HISTORY, TABLE_FAVOURITES, TABLE_FAVOURITE_CATEGORIES)) { private val am = AccountManager.get(context) diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt index 8d8efbf37..df30065f3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt @@ -8,13 +8,13 @@ import android.text.Editable import android.text.TextWatcher import android.view.View import android.widget.Button +import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.transition.Fade import androidx.transition.TransitionManager import com.google.android.material.dialog.MaterialAlertDialogBuilder -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivitySyncAuthBinding @@ -26,7 +26,7 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi private var accountAuthenticatorResponse: AccountAuthenticatorResponse? = null private var resultBundle: Bundle? = null - private val viewModel by viewModel() + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -156,4 +156,4 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi button.isEnabled = text != null && text.length >= 4 } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt index da6dfa32c..160494545 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt @@ -1,12 +1,15 @@ package org.koitharu.kotatsu.sync.ui +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.sync.data.SyncAuthApi import org.koitharu.kotatsu.sync.domain.SyncAuthResult import org.koitharu.kotatsu.utils.SingleLiveEvent -class SyncAuthViewModel( +@HiltViewModel +class SyncAuthViewModel @Inject constructor( private val api: SyncAuthApi, ) : BaseViewModel() { @@ -19,4 +22,4 @@ class SyncAuthViewModel( onTokenObtained.postCall(result) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncProvider.kt b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncProvider.kt index 604e6de9d..05ca3b38e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncProvider.kt @@ -9,13 +9,22 @@ import android.database.sqlite.SQLiteDatabase import android.net.Uri import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteQueryBuilder +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.EntryPointAccessors +import dagger.hilt.components.SingletonComponent import java.util.concurrent.Callable -import org.koin.android.ext.android.inject import org.koitharu.kotatsu.core.db.* abstract class SyncProvider : ContentProvider() { - private val database by inject() + private val database by lazy { + val appContext = checkNotNull(context?.applicationContext) + val entryPoint = EntryPointAccessors.fromApplication(appContext, SyncProviderEntryPoint::class.java) + entryPoint.database() + } + private val supportedTables = setOf( TABLE_FAVOURITES, TABLE_MANGA, @@ -34,7 +43,7 @@ abstract class SyncProvider : ContentProvider() { projection: Array?, selection: String?, selectionArgs: Array?, - sortOrder: String? + sortOrder: String?, ): Cursor? = if (getTableName(uri) != null) { val sqlQuery = SupportSQLiteQueryBuilder.builder(uri.lastPathSegment) .columns(projection) @@ -108,4 +117,10 @@ abstract class SyncProvider : ContentProvider() { val whereArgs = Array(keys.size) { i -> values.get("`${keys[i]}`") ?: values.get(keys[i]) } this.update(table, SQLiteDatabase.CONFLICT_IGNORE, values, whereClause, whereArgs) } -} \ No newline at end of file + + @EntryPoint + @InstallIn(SingletonComponent::class) + interface SyncProviderEntryPoint { + fun database(): MangaDatabase + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/TrackerModule.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/TrackerModule.kt deleted file mode 100644 index e15791927..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/TrackerModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.tracker - -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.tracker.domain.Tracker -import org.koitharu.kotatsu.tracker.domain.TrackingRepository -import org.koitharu.kotatsu.tracker.ui.FeedViewModel -import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels - -val trackerModule - get() = module { - - factory { TrackingRepository(get()) } - factory { TrackerNotificationChannels(androidContext(), get()) } - - factory { Tracker(get(), get(), get()) } - - viewModel { FeedViewModel(get()) } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt index 96aaa9b44..bc66e7789 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.tracker.domain import androidx.annotation.VisibleForTesting +import javax.inject.Inject import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.model.Manga @@ -9,10 +10,11 @@ import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels import org.koitharu.kotatsu.tracker.work.TrackingItem -class Tracker( +class Tracker @Inject constructor( private val settings: AppSettings, private val repository: TrackingRepository, private val channels: TrackerNotificationChannels, + private val mangaRepositoryFactory: MangaRepository.Factory, ) { suspend fun getAllTracks(): List { @@ -67,7 +69,7 @@ class Tracker( } suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates { - val manga = MangaRepository(track.manga.source).getDetails(track.manga) + val manga = mangaRepositoryFactory.create(track.manga.source).getDetails(track.manga) val updates = compare(track, manga) if (commit) { repository.saveUpdates(updates) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/domain/TrackingRepository.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/domain/TrackingRepository.kt index 9f2f77341..ce3f9cda9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/domain/TrackingRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/domain/TrackingRepository.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.tracker.domain import androidx.annotation.VisibleForTesting import androidx.room.withTransaction import java.util.* +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.core.db.MangaDatabase @@ -22,7 +23,7 @@ import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem private const val NO_ID = 0L -class TrackingRepository( +class TrackingRepository @Inject constructor( private val db: MangaDatabase, ) { diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedFragment.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedFragment.kt index c5a9e5b09..8fc48bc5e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedFragment.kt @@ -6,9 +6,11 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding +import androidx.fragment.app.viewModels +import coil.ImageLoader import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener @@ -27,12 +29,16 @@ import org.koitharu.kotatsu.utils.ext.addMenuProvider import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getThemeColor +@AndroidEntryPoint class FeedFragment : BaseFragment(), PaginationScrollListener.Callback, MangaListListener { - private val viewModel by viewModel() + @Inject + lateinit var coil: ImageLoader + + private val viewModel by viewModels() private var feedAdapter: FeedAdapter? = null private var paddingVertical = 0 @@ -40,12 +46,12 @@ class FeedFragment : override fun onInflateView( inflater: LayoutInflater, - container: ViewGroup? + container: ViewGroup?, ) = FragmentFeedBinding.inflate(inflater, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - feedAdapter = FeedAdapter(get(), viewLifecycleOwner, this) + feedAdapter = FeedAdapter(coil, viewLifecycleOwner, this) with(binding.recyclerView) { adapter = feedAdapter setHasFixedSize(true) @@ -55,7 +61,7 @@ class FeedFragment : paddingVertical = resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer) val decoration = TypedSpacingItemDecoration( FeedAdapter.ITEM_TYPE_FEED to 0, - fallbackSpacing = spacing + fallbackSpacing = spacing, ) addItemDecoration(decoration) } @@ -64,7 +70,13 @@ class FeedFragment : setColorSchemeColors(context.getThemeColor(com.google.android.material.R.attr.colorOnPrimary)) isEnabled = false } - addMenuProvider(FeedMenuProvider(binding.recyclerView, (activity as? BottomNavOwner)?.bottomNav ?: binding.recyclerView, viewModel)) + addMenuProvider( + FeedMenuProvider( + binding.recyclerView, + (activity as? BottomNavOwner)?.bottomNav ?: binding.recyclerView, + viewModel, + ), + ) viewModel.content.observe(viewLifecycleOwner, this::onListChanged) viewModel.onError.observe(viewLifecycleOwner, this::onError) @@ -105,7 +117,7 @@ class FeedFragment : val snackbar = Snackbar.make( binding.recyclerView, R.string.updates_feed_cleared, - Snackbar.LENGTH_LONG + Snackbar.LENGTH_LONG, ) snackbar.anchorView = (activity as? BottomNavOwner)?.bottomNav snackbar.show() @@ -115,7 +127,7 @@ class FeedFragment : val snackbar = Snackbar.make( binding.recyclerView, e.getDisplayMessage(resources), - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ) snackbar.anchorView = (activity as? BottomNavOwner)?.bottomNav snackbar.show() diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt index bf89c42ab..6974e053e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt @@ -1,6 +1,10 @@ package org.koitharu.kotatsu.tracker.ui import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.* +import java.util.concurrent.TimeUnit +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin @@ -20,11 +24,10 @@ import org.koitharu.kotatsu.tracker.ui.model.toFeedItem import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.daysDiff -import java.util.* -import java.util.concurrent.TimeUnit -class FeedViewModel( - private val repository: TrackingRepository +@HiltViewModel +class FeedViewModel @Inject constructor( + private val repository: TrackingRepository, ) : BaseViewModel() { private val logList = MutableStateFlow?>(null) @@ -34,7 +37,7 @@ class FeedViewModel( val onFeedCleared = SingleLiveEvent() val content = combine( logList.filterNotNull(), - hasNextPage + hasNextPage, ) { list, isHasNextPage -> buildList(list.size + 2) { if (list.isEmpty()) { @@ -44,7 +47,7 @@ class FeedViewModel( textPrimary = R.string.text_empty_holder_primary, textSecondary = R.string.text_feed_holder, actionStringRes = 0, - ) + ), ) } else { list.mapListTo(this) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index 51aedc866..4cdc2c962 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -9,15 +9,16 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC import androidx.core.app.NotificationCompat.VISIBILITY_SECRET import androidx.core.content.ContextCompat +import androidx.hilt.work.HiltWorker import androidx.lifecycle.LiveData import androidx.lifecycle.map import androidx.work.* import coil.ImageLoader import coil.request.ImageRequest +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject import java.util.concurrent.TimeUnit import kotlinx.coroutines.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.details.ui.DetailsActivity @@ -30,18 +31,19 @@ import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.toBitmapOrNull import org.koitharu.kotatsu.utils.ext.trySetForeground -class TrackWorker(context: Context, workerParams: WorkerParameters) : - CoroutineWorker(context, workerParams), - KoinComponent { +@HiltWorker +class TrackWorker @AssistedInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + private val coil: ImageLoader, + private val settings: AppSettings, + private val tracker: Tracker, +) : CoroutineWorker(context, workerParams) { private val notificationManager by lazy { applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } - private val coil by inject() - private val settings by inject() - private val tracker by inject() - override suspend fun doWork(): Result { if (!settings.isTrackerEnabled) { return Result.success(workDataOf(0, 0)) @@ -105,7 +107,9 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : val colorPrimary = ContextCompat.getColor(applicationContext, R.color.blue_primary) val builder = NotificationCompat.Builder(applicationContext, channelId) val summary = applicationContext.resources.getQuantityString( - R.plurals.new_chapters, newChapters.size, newChapters.size + R.plurals.new_chapters, + newChapters.size, + newChapters.size, ) with(builder) { setContentText(summary) @@ -113,8 +117,8 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : setNumber(newChapters.size) setLargeIcon( coil.execute( - ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build() - ).toBitmapOrNull() + ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build(), + ).toBitmapOrNull(), ) setSmallIcon(R.drawable.ic_stat_book_plus) val style = NotificationCompat.InboxStyle(this) @@ -130,8 +134,8 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : applicationContext, id, intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE, + ), ) setAutoCancel(true) setVisibility(if (manga.isNsfw) VISIBILITY_SECRET else VISIBILITY_PUBLIC) @@ -160,7 +164,9 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : val title = applicationContext.getString(R.string.check_for_new_chapters) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( - WORKER_CHANNEL_ID, title, NotificationManager.IMPORTANCE_LOW + WORKER_CHANNEL_ID, + title, + NotificationManager.IMPORTANCE_LOW, ) channel.setShowBadge(false) channel.enableVibration(false) @@ -217,4 +223,4 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackerNotificationChannels.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackerNotificationChannels.kt index 81fcb73d5..3c100b6a0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackerNotificationChannels.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackerNotificationChannels.kt @@ -7,12 +7,14 @@ import android.content.Context import android.os.Build import androidx.annotation.RequiresApi import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.prefs.AppSettings -class TrackerNotificationChannels( - private val context: Context, +class TrackerNotificationChannels @Inject constructor( + @ApplicationContext private val context: Context, private val settings: AppSettings, ) { @@ -140,4 +142,4 @@ class TrackerNotificationChannels( private const val CHANNEL_ID_HISTORY = "track_history" private const val OLD_CHANNEL_ID = "tracking" } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewModel.kt new file mode 100644 index 000000000..4b73f9503 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewModel.kt @@ -0,0 +1,46 @@ +package org.koitharu.kotatsu.utils.ext + +import androidx.activity.ComponentActivity +import androidx.activity.viewModels +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import androidx.fragment.app.createViewModelLazy +import androidx.fragment.app.viewModels +import androidx.lifecycle.AbstractSavedStateViewModelFactory +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.CreationExtras + +@MainThread +inline fun ComponentActivity.assistedViewModels( + noinline viewModelProducer: (SavedStateHandle) -> VM, +): Lazy = viewModels { + object : AbstractSavedStateViewModelFactory(this, intent.extras) { + override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T { + return requireNotNull(modelClass.cast(viewModelProducer(handle))) + } + } +} + +@MainThread +inline fun Fragment.assistedViewModels( + noinline viewModelProducer: (SavedStateHandle) -> VM, +): Lazy = viewModels { + object : AbstractSavedStateViewModelFactory(this, arguments) { + override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T { + return requireNotNull(modelClass.cast(viewModelProducer(handle))) + } + } +} + +@MainThread +inline fun Fragment.parentFragmentViewModels( + noinline extrasProducer: (() -> CreationExtras)? = null, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null, +): Lazy = createViewModelLazy( + viewModelClass = VM::class, + storeProducer = { requireParentFragment().viewModelStore }, + extrasProducer = { extrasProducer?.invoke() ?: requireParentFragment().defaultViewModelCreationExtras }, + factoryProducer = factoryProducer ?: { requireParentFragment().defaultViewModelProviderFactory }, +) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/image/CoilImageGetter.kt b/app/src/main/java/org/koitharu/kotatsu/utils/image/CoilImageGetter.kt index f88929b23..b9fad7b91 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/image/CoilImageGetter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/image/CoilImageGetter.kt @@ -6,9 +6,11 @@ import android.text.Html import coil.ImageLoader import coil.executeBlocking import coil.request.ImageRequest +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject -class CoilImageGetter( - private val context: Context, +class CoilImageGetter @Inject constructor( + @ApplicationContext private val context: Context, private val coil: ImageLoader, ) : Html.ImageGetter { @@ -17,9 +19,9 @@ class CoilImageGetter( ImageRequest.Builder(context) .data(source) .allowHardware(false) - .build() + .build(), ).drawable?.apply { setBounds(0, 0, intrinsicHeight, intrinsicHeight) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/AppWidgetModule.kt b/app/src/main/java/org/koitharu/kotatsu/widget/AppWidgetModule.kt deleted file mode 100644 index 2e83ca23b..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/widget/AppWidgetModule.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.koitharu.kotatsu.widget - -import androidx.room.InvalidationTracker -import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import org.koitharu.kotatsu.widget.shelf.ShelfConfigViewModel - -val appWidgetModule - get() = module { - - single { WidgetUpdater(androidContext()) } - - viewModel { ShelfConfigViewModel(get()) } - } diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt index 8af7f10f2..6fff7b8ac 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt @@ -5,13 +5,17 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.room.InvalidationTracker +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES import org.koitharu.kotatsu.core.db.TABLE_HISTORY import org.koitharu.kotatsu.widget.recent.RecentWidgetProvider import org.koitharu.kotatsu.widget.shelf.ShelfWidgetProvider -class WidgetUpdater( - private val context: Context +@Singleton +class WidgetUpdater @Inject constructor( + @ApplicationContext private val context: Context, ) : InvalidationTracker.Observer(TABLE_HISTORY, TABLE_FAVOURITES) { override fun onInvalidated(tables: MutableSet) { diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt index ccad1811d..250892e28 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt @@ -2,11 +2,21 @@ package org.koitharu.kotatsu.widget.recent import android.content.Intent import android.widget.RemoteViewsService -import org.koin.android.ext.android.get +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.history.domain.HistoryRepository +import javax.inject.Inject +@AndroidEntryPoint class RecentWidgetService : RemoteViewsService() { + @Inject + lateinit var historyRepository: HistoryRepository + + @Inject + lateinit var coil: ImageLoader + override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { - return RecentListFactory(applicationContext, get(), get()) + return RecentListFactory(applicationContext, historyRepository, coil) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt index 44a2cc632..55dba682c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigActivity.kt @@ -6,12 +6,13 @@ import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup +import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding +import com.google.android.material.R as materialR import com.google.android.material.snackbar.Snackbar -import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -20,12 +21,13 @@ import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.widget.shelf.adapter.CategorySelectAdapter import org.koitharu.kotatsu.widget.shelf.model.CategoryItem -import com.google.android.material.R as materialR -class ShelfConfigActivity : BaseActivity(), - OnListItemClickListener, View.OnClickListener { +class ShelfConfigActivity : + BaseActivity(), + OnListItemClickListener, + View.OnClickListener { - private val viewModel by viewModel() + private val viewModel by viewModels() private lateinit var adapter: CategorySelectAdapter private lateinit var config: AppWidgetConfig @@ -44,7 +46,7 @@ class ShelfConfigActivity : BaseActivity(), binding.fabAdd.hide() val appWidgetId = intent?.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID + AppWidgetManager.INVALID_APPWIDGET_ID, ) ?: AppWidgetManager.INVALID_APPWIDGET_ID if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finishAfterTransition() @@ -64,7 +66,7 @@ class ShelfConfigActivity : BaseActivity(), updateWidget() setResult( Activity.RESULT_OK, - Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, config.widgetId) + Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, config.widgetId), ) finish() } @@ -84,12 +86,12 @@ class ShelfConfigActivity : BaseActivity(), binding.recyclerView.updatePadding( left = insets.left, right = insets.right, - bottom = insets.bottom + bottom = insets.bottom, ) with(binding.toolbar) { updatePadding( left = insets.left, - right = insets.right + right = insets.right, ) updateLayoutParams { topMargin = insets.top diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt index 6804f8967..9c3772efb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt @@ -2,6 +2,8 @@ package org.koitharu.kotatsu.widget.shelf import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -10,15 +12,16 @@ import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.widget.shelf.model.CategoryItem -class ShelfConfigViewModel( - favouritesRepository: FavouritesRepository +@HiltViewModel +class ShelfConfigViewModel @Inject constructor( + favouritesRepository: FavouritesRepository, ) : BaseViewModel() { private val selectedCategoryId = MutableStateFlow(0L) val content: LiveData> = combine( favouritesRepository.observeCategories(), - selectedCategoryId + selectedCategoryId, ) { categories, selectedId -> val list = ArrayList(categories.size + 1) list += CategoryItem(0L, null, selectedId == 0L) @@ -29,4 +32,4 @@ class ShelfConfigViewModel( }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) var checkedId: Long by selectedCategoryId::value -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt index 89d0a8862..40c0d86bb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt @@ -3,15 +3,25 @@ package org.koitharu.kotatsu.widget.shelf import android.appwidget.AppWidgetManager import android.content.Intent import android.widget.RemoteViewsService -import org.koin.android.ext.android.get +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +@AndroidEntryPoint class ShelfWidgetService : RemoteViewsService() { + @Inject + lateinit var favouritesRepository: FavouritesRepository + + @Inject + lateinit var coil: ImageLoader + override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { val widgetId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID + AppWidgetManager.INVALID_APPWIDGET_ID, ) - return ShelfListFactory(applicationContext, get(), get(), widgetId) + return ShelfListFactory(applicationContext, favouritesRepository, coil, widgetId) } -} \ No newline at end of file +} diff --git a/build.gradle b/build.gradle index d92058634..a877a7cdd 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.2.1' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' + classpath 'com.google.dagger:hilt-android-gradle-plugin:2.42' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -44,4 +45,4 @@ String currentBranch() { task clean(type: Delete) { delete rootProject.buildDir -} \ No newline at end of file +}