From 26b852365a2d63caf5a5e186c1ef298cd8d9bf3c Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 10 Feb 2023 20:36:59 +0200 Subject: [PATCH] Scrobblers config activity --- app/src/main/AndroidManifest.xml | 27 ++- .../base/domain/MangaDataRepository.kt | 18 +- .../koitharu/kotatsu/core/db/MangaDatabase.kt | 19 +- .../koitharu/kotatsu/core/ui/DateTimeAgo.kt | 12 +- .../kotatsu/details/ui/DetailsFragment.kt | 2 +- .../kotatsu/details/ui/DetailsMenuProvider.kt | 2 +- .../kotatsu/details/ui/DetailsViewModel.kt | 6 +- .../details/ui/scrobbling/ScrobblingInfoAD.kt | 2 +- .../scrobbling/ScrobblingInfoBottomSheet.kt | 6 +- .../ui/scrobbling/ScrollingInfoAdapter.kt | 4 +- .../kotatsu/explore/ui/model/ExploreItem.kt | 5 +- .../history/domain/HistoryRepository.kt | 4 +- .../list/ui/adapter/EmptyStateListAD.kt | 11 +- .../kotatsu/list/ui/model/ListModel.kt | 5 +- .../kotatsu/list/ui/model/LoadingFooter.kt | 5 +- .../kotatsu/list/ui/model/LoadingState.kt | 5 +- .../kotatsu/scrobbling/ScrobblingModule.kt | 8 +- .../anilist/data/AniListAuthenticator.kt | 6 +- .../anilist/data/AniListInterceptor.kt | 2 +- .../anilist/data/AniListRepository.kt | 15 +- .../anilist/domain/AniListScrobbler.kt | 6 +- .../anilist/ui/AniListSettingsFragment.kt | 86 -------- .../anilist/ui/AniListSettingsViewModel.kt | 57 ------ .../{ => common}/data/ScrobblerRepository.kt | 8 +- .../{ => common}/data/ScrobblerStorage.kt | 6 +- .../{ => common}/data/ScrobblingDao.kt | 5 +- .../{ => common}/data/ScrobblingEntity.kt | 2 +- .../{ => common}/domain/Scrobbler.kt | 63 ++++-- .../domain/model/ScrobblerManga.kt | 4 +- .../domain/model/ScrobblerMangaInfo.kt | 4 +- .../domain/model/ScrobblerService.kt | 2 +- .../domain/model/ScrobblerType.kt | 2 +- .../domain/model/ScrobblerUser.kt | 2 +- .../domain/model/ScrobblingInfo.kt | 8 +- .../common/domain/model/ScrobblingStatus.kt | 13 ++ .../ui/config/ScrobblerConfigActivity.kt | 188 ++++++++++++++++++ .../ui/config/ScrobblerConfigViewModel.kt | 98 +++++++++ .../ui/config/adapter/ScrobblingHeaderAD.kt | 16 ++ .../ui/config/adapter/ScrobblingMangaAD.kt | 42 ++++ .../config/adapter/ScrobblingMangaAdapter.kt | 48 +++++ .../selector/ScrobblingSelectorBottomSheet.kt | 10 +- .../selector/ScrobblingSelectorViewModel.kt | 8 +- .../ui/selector/adapter/ScrobblerHintAD.kt | 4 +- .../ScrobblerMangaSelectionDecoration.kt | 4 +- .../adapter/ScrobblerSelectorAdapter.kt | 6 +- .../ui/selector/adapter/ScrobblingMangaAD.kt | 4 +- .../ui/selector/model/ScrobblerHint.kt | 2 +- .../domain/model/ScrobblingStatus.kt | 11 - .../scrobbling/mal/data/MALAuthenticator.kt | 6 +- .../scrobbling/mal/data/MALInterceptor.kt | 2 +- .../scrobbling/mal/data/MALRepository.kt | 48 ++--- .../scrobbling/mal/domain/MALScrobbler.kt | 9 +- .../scrobbling/mal/ui/MALSettingsFragment.kt | 86 -------- .../scrobbling/mal/ui/MALSettingsViewModel.kt | 57 ------ .../shikimori/data/ShikimoriAuthenticator.kt | 6 +- .../shikimori/data/ShikimoriInterceptor.kt | 2 +- .../shikimori/data/ShikimoriRepository.kt | 14 +- .../shikimori/domain/ShikimoriScrobbler.kt | 6 +- .../shikimori/ui/ShikimoriSettingsFragment.kt | 86 -------- .../ui/ShikimoriSettingsViewModel.kt | 57 ------ .../settings/HistorySettingsFragment.kt | 22 +- .../kotatsu/settings/SettingsActivity.kt | 29 --- .../kotatsu/tracker/ui/feed/FeedFragment.kt | 4 - .../org/koitharu/kotatsu/utils/ext/FlowExt.kt | 9 +- ...{ic_shikimori.png => ic_shikimori_raw.png} | Bin ...{ic_shikimori.png => ic_shikimori_raw.png} | Bin ...{ic_shikimori.png => ic_shikimori_raw.png} | Bin ...{ic_shikimori.png => ic_shikimori_raw.png} | Bin ...{ic_shikimori.png => ic_shikimori_raw.png} | Bin app/src/main/res/drawable/ic_anilist.xml | 1 + app/src/main/res/drawable/ic_mal.xml | 1 + app/src/main/res/drawable/ic_shikimori.xml | 5 + .../res/layout/activity_scrobbler_config.xml | 74 +++++++ .../main/res/layout/item_scrobbling_manga.xml | 51 +++++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/pref_anilist.xml | 19 -- app/src/main/res/xml/pref_history.xml | 25 +-- app/src/main/res/xml/pref_mal.xml | 19 -- app/src/main/res/xml/pref_shikimori.xml | 19 -- 79 files changed, 814 insertions(+), 718 deletions(-) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsFragment.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsViewModel.kt rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/data/ScrobblerRepository.kt (71%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/data/ScrobblerStorage.kt (88%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/data/ScrobblingDao.kt (77%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/data/ScrobblingEntity.kt (94%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/Scrobbler.kt (62%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblerManga.kt (93%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblerMangaInfo.kt (67%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblerService.kt (86%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblerType.kt (64%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblerUser.kt (91%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/domain/model/ScrobblingInfo.kt (92%) create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingStatus.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/ScrobblingSelectorBottomSheet.kt (94%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/ScrobblingSelectorViewModel.kt (94%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/adapter/ScrobblerHintAD.kt (88%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt (90%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/adapter/ScrobblerSelectorAdapter.kt (87%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/adapter/ScrobblingMangaAD.kt (90%) rename app/src/main/java/org/koitharu/kotatsu/scrobbling/{ => common}/ui/selector/model/ScrobblerHint.kt (93%) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingStatus.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt rename app/src/main/res/drawable-hdpi/{ic_shikimori.png => ic_shikimori_raw.png} (100%) rename app/src/main/res/drawable-mdpi/{ic_shikimori.png => ic_shikimori_raw.png} (100%) rename app/src/main/res/drawable-xhdpi/{ic_shikimori.png => ic_shikimori_raw.png} (100%) rename app/src/main/res/drawable-xxhdpi/{ic_shikimori.png => ic_shikimori_raw.png} (100%) rename app/src/main/res/drawable-xxxhdpi/{ic_shikimori.png => ic_shikimori_raw.png} (100%) create mode 100644 app/src/main/res/drawable/ic_shikimori.xml create mode 100644 app/src/main/res/layout/activity_scrobbler_config.xml create mode 100644 app/src/main/res/layout/item_scrobbling_manga.xml delete mode 100644 app/src/main/res/xml/pref_anilist.xml delete mode 100644 app/src/main/res/xml/pref_mal.xml delete mode 100644 app/src/main/res/xml/pref_shikimori.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3f2bd0ab..5483b2063 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -85,16 +85,7 @@ - - - - - - - - - + android:label="@string/settings" /> + + + + + + + + + + + + ( { inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }, ) { - binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() } + + if (listener != null) { + binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() } + } bind { binding.icon.newImageRequest(item.icon)?.enqueueWith(coil) binding.textPrimary.setText(item.textPrimary) binding.textSecondary.setTextAndVisible(item.textSecondary) - binding.buttonRetry.setTextAndVisible(item.actionStringRes) + if (listener != null) { + binding.buttonRetry.setTextAndVisible(item.actionStringRes) + } } onViewRecycled { diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModel.kt index 1ae2536f9..ba16c8278 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModel.kt @@ -1,3 +1,6 @@ package org.koitharu.kotatsu.list.ui.model -interface ListModel \ No newline at end of file +interface ListModel { + + override fun equals(other: Any?): Boolean +} diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingFooter.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingFooter.kt index 97a63b6a6..7a754dcc6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingFooter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingFooter.kt @@ -1,3 +1,6 @@ package org.koitharu.kotatsu.list.ui.model -object LoadingFooter : ListModel \ No newline at end of file +object LoadingFooter : ListModel { + + override fun equals(other: Any?): Boolean = other === LoadingFooter +} diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingState.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingState.kt index a866e7427..ae0a3088e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingState.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/LoadingState.kt @@ -1,3 +1,6 @@ package org.koitharu.kotatsu.list.ui.model -object LoadingState : ListModel \ No newline at end of file +object LoadingState : ListModel { + + override fun equals(other: Any?): Boolean = other === LoadingState +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt index 6145227fc..18d484565 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt @@ -15,14 +15,14 @@ import org.koitharu.kotatsu.scrobbling.anilist.data.AniListAuthenticator import org.koitharu.kotatsu.scrobbling.anilist.data.AniListInterceptor import org.koitharu.kotatsu.scrobbling.anilist.data.AniListRepository import org.koitharu.kotatsu.scrobbling.anilist.domain.AniListScrobbler -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType import org.koitharu.kotatsu.scrobbling.mal.data.MALAuthenticator import org.koitharu.kotatsu.scrobbling.mal.data.MALInterceptor import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository import org.koitharu.kotatsu.scrobbling.mal.domain.MALScrobbler -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriAuthenticator import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriInterceptor import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListAuthenticator.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListAuthenticator.kt index 5fb3040d5..57f9fc517 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListAuthenticator.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListAuthenticator.kt @@ -7,9 +7,9 @@ import okhttp3.Response import okhttp3.Route import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType import javax.inject.Inject import javax.inject.Provider diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListInterceptor.kt index 9812283fb..25045c83e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListInterceptor.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.scrobbling.anilist.data import okhttp3.Interceptor import okhttp3.Response import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage private const val JSON = "application/json" diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListRepository.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListRepository.kt index b1a12c51c..571a8bef0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/data/AniListRepository.kt @@ -15,13 +15,12 @@ import org.koitharu.kotatsu.parsers.util.json.getStringOrNull import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.parseJson import org.koitharu.kotatsu.parsers.util.toIntUp -import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser import kotlin.math.roundToInt private const val REDIRECT_URI = "kotatsu://anilist-auth" @@ -36,7 +35,7 @@ class AniListRepository( private val okHttp: OkHttpClient, private val storage: ScrobblerStorage, private val db: MangaDatabase, -) : ScrobblerRepository { +) : org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository { override val oauthUrl: String get() = "${BASE_URL}oauth/authorize?client_id=${BuildConfig.ANILIST_CLIENT_ID}&" + diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/domain/AniListScrobbler.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/domain/AniListScrobbler.kt index 967a0dc3b..05b10da0a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/domain/AniListScrobbler.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/domain/AniListScrobbler.kt @@ -2,9 +2,9 @@ package org.koitharu.kotatsu.scrobbling.anilist.domain import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.scrobbling.anilist.data.AniListRepository -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsFragment.kt deleted file mode 100644 index 5a98b7a81..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.anilist.ui - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.preference.Preference -import coil.ImageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation -import dagger.hilt.android.AndroidEntryPoint -import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.base.ui.BasePreferenceFragment -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser -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 -import javax.inject.Inject - -@AndroidEntryPoint -class AniListSettingsFragment : BasePreferenceFragment(R.string.anilist) { - - @Inject - lateinit var coil: ImageLoader - - @Inject - lateinit var viewModelFactory: AniListSettingsViewModel.Factory - - private val viewModel by assistedViewModels { - viewModelFactory.create(arguments?.getString(ARG_AUTH_CODE)) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.pref_anilist) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.user.observe(viewLifecycleOwner, this::onUserChanged) - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - return when (preference.key) { - KEY_USER -> openAuthorization() - KEY_LOGOUT -> { - viewModel.logout() - true - } - - else -> super.onPreferenceTreeClick(preference) - } - } - - private fun onUserChanged(user: ScrobblerUser?) { - val pref = findPreference(KEY_USER) ?: return - pref.isSelectable = user == null - pref.title = user?.nickname ?: getString(R.string.sign_in) - ImageRequest.Builder(requireContext()) - .data(user?.avatar) - .transformations(CircleCropTransformation()) - .target(PreferenceIconTarget(pref)) - .enqueueWith(coil) - findPreference(KEY_LOGOUT)?.isVisible = user != null - } - - private fun openAuthorization(): Boolean { - return runCatching { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(viewModel.authorizationUrl) - startActivity(intent) - }.isSuccess - } - - companion object { - - private const val KEY_USER = "al_user" - private const val KEY_LOGOUT = "al_logout" - - private const val ARG_AUTH_CODE = "auth_code" - - fun newInstance(authCode: String?) = AniListSettingsFragment().withArgs(1) { - putString(ARG_AUTH_CODE, authCode) - } - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsViewModel.kt deleted file mode 100644 index 554e5a219..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/anilist/ui/AniListSettingsViewModel.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.anilist.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.anilist.data.AniListRepository -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser - -class AniListSettingsViewModel @AssistedInject constructor( - private val repository: AniListRepository, - @Assisted authCode: String?, -) : BaseViewModel() { - - val authorizationUrl: String - get() = repository.oauthUrl - - val user = MutableLiveData() - - init { - if (authCode != null) { - authorize(authCode) - } else { - loadUser() - } - } - - fun logout() { - launchJob(Dispatchers.Default) { - repository.logout() - user.postValue(null) - } - } - - private fun loadUser() = launchJob(Dispatchers.Default) { - val userModel = if (repository.isAuthorized) { - repository.cachedUser?.let(user::postValue) - repository.loadUser() - } else { - null - } - user.postValue(userModel) - } - - private fun authorize(code: String) = launchJob(Dispatchers.Default) { - repository.authorize(code) - user.postValue(repository.loadUser()) - } - - @AssistedFactory - interface Factory { - - fun create(authCode: String?): AniListSettingsViewModel - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerRepository.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerRepository.kt similarity index 71% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerRepository.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerRepository.kt index 08310ae25..35ed7bd46 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerRepository.kt @@ -1,9 +1,9 @@ -package org.koitharu.kotatsu.scrobbling.data +package org.koitharu.kotatsu.scrobbling.common.data import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser interface ScrobblerRepository { diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerStorage.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerStorage.kt similarity index 88% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerStorage.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerStorage.kt index c5f7973c5..b5ab50d02 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblerStorage.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblerStorage.kt @@ -1,10 +1,10 @@ -package org.koitharu.kotatsu.scrobbling.data +package org.koitharu.kotatsu.scrobbling.common.data import android.content.Context import androidx.core.content.edit import org.jsoup.internal.StringUtil.StringJoiner -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser private const val KEY_ACCESS_TOKEN = "access_token" private const val KEY_REFRESH_TOKEN = "refresh_token" diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingDao.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt similarity index 77% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingDao.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt index 812393dee..c24a93b55 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.data +package org.koitharu.kotatsu.scrobbling.common.data import androidx.room.* import kotlinx.coroutines.flow.Flow @@ -12,6 +12,9 @@ abstract class ScrobblingDao { @Query("SELECT * FROM scrobblings WHERE scrobbler = :scrobbler AND manga_id = :mangaId") abstract fun observe(scrobbler: Int, mangaId: Long): Flow + @Query("SELECT * FROM scrobblings WHERE scrobbler = :scrobbler") + abstract fun observe(scrobbler: Int): Flow> + @Upsert abstract suspend fun upsert(entity: ScrobblingEntity) diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingEntity.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingEntity.kt similarity index 94% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingEntity.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingEntity.kt index 5a18cf488..32764e32b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/data/ScrobblingEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingEntity.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.data +package org.koitharu.kotatsu.scrobbling.common.data import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/Scrobbler.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/Scrobbler.kt similarity index 62% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/Scrobbler.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/Scrobbler.kt index 54f9ac504..5eb3ebec7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/Scrobbler.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/Scrobbler.kt @@ -1,19 +1,23 @@ -package org.koitharu.kotatsu.scrobbling.domain +package org.koitharu.kotatsu.scrobbling.common.domain import androidx.collection.LongSparseArray import androidx.collection.getOrElse import androidx.core.text.parseAsHtml +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository -import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus import org.koitharu.kotatsu.utils.ext.findKeyByValue import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.runCatchingCancellable @@ -22,15 +26,37 @@ import java.util.EnumMap abstract class Scrobbler( protected val db: MangaDatabase, val scrobblerService: ScrobblerService, - private val repository: ScrobblerRepository, + private val repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository, ) { private val infoCache = LongSparseArray() protected val statuses = EnumMap(ScrobblingStatus::class.java) + val user: Flow = flow { + repository.cachedUser?.let { + emit(it) + } + runCatchingCancellable { + repository.loadUser() + }.onSuccess { + emit(it) + }.onFailure { + it.printStackTraceDebug() + } + } + val isAvailable: Boolean get() = repository.isAuthorized + suspend fun authorize(authCode: String): ScrobblerUser { + repository.authorize(authCode) + return repository.loadUser() + } + + suspend fun logout() { + repository.logout() + } + suspend fun findManga(query: String, offset: Int): List { return repository.findManga(query, offset) } @@ -46,14 +72,27 @@ abstract class Scrobbler( suspend fun getScrobblingInfoOrNull(mangaId: Long): ScrobblingInfo? { val entity = db.scrobblingDao.find(scrobblerService.id, mangaId) ?: return null - return entity.toScrobblingInfo(mangaId) + return entity.toScrobblingInfo() } abstract suspend fun updateScrobblingInfo(mangaId: Long, rating: Float, status: ScrobblingStatus?, comment: String?) fun observeScrobblingInfo(mangaId: Long): Flow { return db.scrobblingDao.observe(scrobblerService.id, mangaId) - .map { it?.toScrobblingInfo(mangaId) } + .map { it?.toScrobblingInfo() } + } + + fun observeAllScrobblingInfo(): Flow> { + return db.scrobblingDao.observe(scrobblerService.id) + .map { entities -> + coroutineScope { + entities.map { + async { + it.toScrobblingInfo() + } + }.awaitAll() + }.filterNotNull() + } } suspend fun unregisterScrobbling(mangaId: Long) { @@ -64,7 +103,7 @@ abstract class Scrobbler( return repository.getMangaInfo(id) } - private suspend fun ScrobblingEntity.toScrobblingInfo(mangaId: Long): ScrobblingInfo? { + private suspend fun ScrobblingEntity.toScrobblingInfo(): ScrobblingInfo? { val mangaInfo = infoCache.getOrElse(targetId) { runCatchingCancellable { getMangaInfo(targetId) diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerManga.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerManga.kt similarity index 93% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerManga.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerManga.kt index 9e28c9d7d..b00da2c8d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerManga.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerManga.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model import org.koitharu.kotatsu.list.ui.model.ListModel @@ -37,4 +37,4 @@ class ScrobblerManga( override fun toString(): String { return "ScrobblerManga #$id \"$name\" $url" } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerMangaInfo.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerMangaInfo.kt similarity index 67% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerMangaInfo.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerMangaInfo.kt index 940262041..3aa638053 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerMangaInfo.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerMangaInfo.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model class ScrobblerMangaInfo( val id: Long, @@ -6,4 +6,4 @@ class ScrobblerMangaInfo( val cover: String, val url: String, val descriptionHtml: String, -) \ No newline at end of file +) diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerService.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerService.kt similarity index 86% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerService.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerService.kt index b8819b6fb..5b47a3a25 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerService.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model import androidx.annotation.DrawableRes import androidx.annotation.StringRes diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerType.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerType.kt similarity index 64% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerType.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerType.kt index e2d372edb..8920e295a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerType.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerType.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model import javax.inject.Qualifier diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerUser.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerUser.kt similarity index 91% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerUser.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerUser.kt index 92ecce0df..73f37ce3e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblerUser.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblerUser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model class ScrobblerUser( val id: Long, diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingInfo.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingInfo.kt similarity index 92% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingInfo.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingInfo.kt index 87393d6ec..5dd798f33 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingInfo.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingInfo.kt @@ -1,4 +1,6 @@ -package org.koitharu.kotatsu.scrobbling.domain.model +package org.koitharu.kotatsu.scrobbling.common.domain.model + +import org.koitharu.kotatsu.list.ui.model.ListModel class ScrobblingInfo( val scrobbler: ScrobblerService, @@ -12,7 +14,7 @@ class ScrobblingInfo( val coverUrl: String, val description: CharSequence?, val externalUrl: String, -) { +) : ListModel { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -49,4 +51,4 @@ class ScrobblingInfo( result = 31 * result + externalUrl.hashCode() return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingStatus.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingStatus.kt new file mode 100644 index 000000000..70118d585 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/domain/model/ScrobblingStatus.kt @@ -0,0 +1,13 @@ +package org.koitharu.kotatsu.scrobbling.common.domain.model + +import org.koitharu.kotatsu.list.ui.model.ListModel + +enum class ScrobblingStatus : ListModel { + + PLANNED, + READING, + RE_READING, + COMPLETED, + ON_HOLD, + DROPPED, +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt new file mode 100644 index 000000000..00fa4d423 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt @@ -0,0 +1,188 @@ +package org.koitharu.kotatsu.scrobbling.common.ui.config + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.core.graphics.Insets +import androidx.core.view.isVisible +import androidx.core.view.updatePadding +import coil.ImageLoader +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.base.ui.list.decor.TypedSpacingItemDecoration +import org.koitharu.kotatsu.databinding.ActivityScrobblerConfigBinding +import org.koitharu.kotatsu.details.ui.DetailsActivity +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.scrobbling.common.ui.config.adapter.ScrobblingMangaAdapter +import org.koitharu.kotatsu.tracker.ui.feed.adapter.FeedAdapter +import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.hideCompat +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.showCompat +import javax.inject.Inject + +@AndroidEntryPoint +class ScrobblerConfigActivity : BaseActivity(), + OnListItemClickListener, View.OnClickListener { + + @Inject + lateinit var viewModelFactory: ScrobblerConfigViewModel.Factory + + @Inject + lateinit var coil: ImageLoader + + private val viewModel: ScrobblerConfigViewModel by assistedViewModels { + viewModelFactory.create(requireNotNull(getScrobblerService(intent))) + } + + private var paddingVertical = 0 + private var paddingHorizontal = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityScrobblerConfigBinding.inflate(layoutInflater)) + setTitle(viewModel.titleResId) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + val listAdapter = ScrobblingMangaAdapter(this, coil, this) + with(binding.recyclerView) { + adapter = listAdapter + setHasFixedSize(true) + val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing) + paddingHorizontal = spacing + paddingVertical = resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer) + val decoration = TypedSpacingItemDecoration( + FeedAdapter.ITEM_TYPE_FEED to 0, + fallbackSpacing = spacing, + ) + addItemDecoration(decoration) + } + binding.imageViewAvatar.setOnClickListener(this) + + viewModel.content.observe(this, listAdapter::setItems) + viewModel.user.observe(this, this::onUserChanged) + viewModel.isLoading.observe(this, this::onLoadingStateChanged) + viewModel.onError.observe(this, this::onError) + viewModel.onLoggedOut.observe(this) { + finishAfterTransition() + } + + processIntent(intent) + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + if (intent != null) { + setIntent(intent) + processIntent(intent) + } + } + + override fun onWindowInsetsChanged(insets: Insets) { + binding.recyclerView.updatePadding( + left = insets.left + paddingHorizontal, + right = insets.right + paddingHorizontal, + bottom = insets.bottom + paddingVertical, + ) + } + + override fun onItemClick(item: ScrobblingInfo, view: View) { + startActivity( + DetailsActivity.newIntent(this, item.mangaId), + ) + } + + override fun onClick(v: View) { + when (v.id) { + R.id.imageView_avatar -> showUserDialog() + } + } + + private fun processIntent(intent: Intent) { + if (intent.action == Intent.ACTION_VIEW) { + val uri = intent.data ?: return + val code = uri.getQueryParameter("code") + if (!code.isNullOrEmpty()) { + viewModel.onAuthCodeReceived(code) + } + } + } + + private fun onUserChanged(user: ScrobblerUser?) { + if (user == null) { + binding.imageViewAvatar.disposeImageRequest() + binding.imageViewAvatar.isVisible = false + return + } + binding.imageViewAvatar.isVisible = true + binding.imageViewAvatar.newImageRequest(user.avatar, null) + ?.enqueueWith(coil) + } + + private fun onLoadingStateChanged(isLoading: Boolean) { + binding.progressBar.run { + if (isLoading) { + showCompat() + } else { + hideCompat() + } + } + } + + private fun onError(e: Throwable) { + Snackbar.make( + binding.recyclerView, + e.getDisplayMessage(resources), + Snackbar.LENGTH_LONG, + ).show() + } + + private fun showUserDialog() { + MaterialAlertDialogBuilder(this) + .setTitle(title) + .setMessage(getString(R.string.logged_in_as, viewModel.user.value?.nickname)) + .setNegativeButton(R.string.close, null) + .setPositiveButton(R.string.logout) { _, _ -> + viewModel.logout() + }.show() + } + + companion object { + + private const val EXTRA_SERVICE_ID = "service" + + private const val HOST_SHIKIMORI_AUTH = "shikimori-auth" + private const val HOST_ANILIST_AUTH = "anilist-auth" + private const val HOST_MAL_AUTH = "mal-auth" + + fun newIntent(context: Context, service: ScrobblerService) = + Intent(context, ScrobblerConfigActivity::class.java) + .putExtra(EXTRA_SERVICE_ID, service.id) + + private fun getScrobblerService( + intent: Intent + ): ScrobblerService? { + val serviceId = intent.getIntExtra(EXTRA_SERVICE_ID, 0) + if (serviceId != 0) { + return enumValues().first { it.id == serviceId } + } + val uri = intent.data ?: return null + return when (uri.host) { + HOST_SHIKIMORI_AUTH -> ScrobblerService.SHIKIMORI + HOST_ANILIST_AUTH -> ScrobblerService.ANILIST + HOST_MAL_AUTH -> ScrobblerService.MAL + else -> null + } + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt new file mode 100644 index 000000000..c4c3ac324 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt @@ -0,0 +1,98 @@ +package org.koitharu.kotatsu.scrobbling.common.ui.config + +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.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.plus +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.list.ui.model.EmptyState +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus +import org.koitharu.kotatsu.utils.SingleLiveEvent +import org.koitharu.kotatsu.utils.asFlowLiveData +import org.koitharu.kotatsu.utils.ext.onFirst + +class ScrobblerConfigViewModel @AssistedInject constructor( + @Assisted scrobblerService: ScrobblerService, + scrobblers: Set<@JvmSuppressWildcards Scrobbler>, +) : BaseViewModel() { + + private val scrobbler = scrobblers.first { it.scrobblerService == scrobblerService } + + val titleResId = scrobbler.scrobblerService.titleResId + + val user = MutableLiveData(null) + val onLoggedOut = SingleLiveEvent() + + val content = scrobbler.observeAllScrobblingInfo() + .onStart { loadingCounter.increment() } + .onFirst { loadingCounter.decrement() } + .catch { errorEvent.postCall(it) } + .map { buildContentList(it) } + .asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) + + init { + scrobbler.user + .onEach { user.postValue(it) } + .launchIn(viewModelScope + Dispatchers.Default) + } + + fun onAuthCodeReceived(authCode: String) { + launchLoadingJob(Dispatchers.Default) { + val newUser = scrobbler.authorize(authCode) + user.postValue(newUser) + } + } + + fun logout() { + launchLoadingJob(Dispatchers.Default) { + scrobbler.logout() + user.postValue(null) + onLoggedOut.postCall(Unit) + } + } + + private fun buildContentList(list: List): List { + if (list.isEmpty()) { + return listOf( + EmptyState( + icon = R.drawable.ic_empty_history, + textPrimary = R.string.nothing_here, + textSecondary = R.string.scrobbling_empty_hint, + actionStringRes = 0, + ), + ) + } + val grouped = list.groupBy { it.status } + val statuses = enumValues() + val result = ArrayList(list.size + statuses.size) + for (st in statuses) { + val subList = grouped[st] + if (subList.isNullOrEmpty()) { + continue + } + result.add(st) + result.addAll(subList) + } + return result + } + + @AssistedFactory + interface Factory { + + fun create(service: ScrobblerService): ScrobblerConfigViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt new file mode 100644 index 000000000..cb233abf0 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt @@ -0,0 +1,16 @@ +package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter + +import android.widget.TextView +import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus + +fun scrobblingHeaderAD() = adapterDelegate(R.layout.item_header) { + + bind { + (itemView as TextView).text = context.resources + .getStringArray(R.array.scrobbling_statuses) + .getOrNull(item.ordinal) + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt new file mode 100644 index 000000000..fb04299f4 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt @@ -0,0 +1,42 @@ +package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter + +import androidx.lifecycle.LifecycleOwner +import coil.ImageLoader +import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter +import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.databinding.ItemScrobblingMangaBinding +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest + +fun scrobblingMangaAD( + clickListener: OnListItemClickListener, + coil: ImageLoader, + lifecycleOwner: LifecycleOwner, +) = adapterDelegateViewBinding( + { layoutInflater, parent -> ItemScrobblingMangaBinding.inflate(layoutInflater, parent, false) }, +) { + + val clickListenerAdapter = AdapterDelegateClickListenerAdapter(this, clickListener) + itemView.setOnClickListener(clickListenerAdapter) + + bind { + binding.imageViewCover.newImageRequest(item.coverUrl, null)?.run { + placeholder(R.drawable.ic_placeholder) + fallback(R.drawable.ic_placeholder) + error(R.drawable.ic_error_placeholder) + lifecycle(lifecycleOwner) + enqueueWith(coil) + } + binding.textViewTitle.text = item.title + binding.ratingBar.rating = item.rating * binding.ratingBar.numStars + } + + onViewRecycled { + binding.imageViewCover.disposeImageRequest() + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt new file mode 100644 index 000000000..301836728 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt @@ -0,0 +1,48 @@ +package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter + +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.DiffUtil +import coil.ImageLoader +import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter +import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD +import org.koitharu.kotatsu.list.ui.model.EmptyState +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus + +class ScrobblingMangaAdapter( + clickListener: OnListItemClickListener, + coil: ImageLoader, + lifecycleOwner: LifecycleOwner, +) : AsyncListDifferDelegationAdapter(DiffCallback()) { + + init { + delegatesManager.addDelegate(scrobblingMangaAD(clickListener, coil, lifecycleOwner)) + .addDelegate(scrobblingHeaderAD()) + .addDelegate(emptyStateListAD(coil, null)) + } + + private class DiffCallback : DiffUtil.ItemCallback() { + + override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel): Boolean { + return when { + oldItem is ScrobblingInfo && newItem is ScrobblingInfo -> { + oldItem.targetId == newItem.targetId && oldItem.mangaId == newItem.mangaId + } + + oldItem is ScrobblingStatus && newItem is ScrobblingStatus -> { + oldItem.ordinal == newItem.ordinal + } + + oldItem is EmptyState && newItem is EmptyState -> true + + else -> false + } + } + + override fun areContentsTheSame(oldItem: ListModel, newItem: ListModel): Boolean { + return oldItem == newItem + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt similarity index 94% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt index 32b84b4ca..488bdd57e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector +package org.koitharu.kotatsu.scrobbling.common.ui.selector import android.app.Dialog import android.content.DialogInterface @@ -24,10 +24,10 @@ import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.databinding.SheetScrobblingSelectorBinding import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ScrobblerMangaSelectionDecoration -import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ScrobblerSelectorAdapter +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter.ScrobblerMangaSelectionDecoration +import org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter.ScrobblerSelectorAdapter import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition import org.koitharu.kotatsu.utils.ext.getDisplayMessage diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt similarity index 94% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt index 6c8a6ada1..318d2a2f8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/ScrobblingSelectorViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector +package org.koitharu.kotatsu.scrobbling.common.ui.selector import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -18,9 +18,9 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.ui.selector.model.ScrobblerHint +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.ui.selector.model.ScrobblerHint import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.printStackTraceDebug diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerHintAD.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerHintAD.kt similarity index 88% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerHintAD.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerHintAD.kt index 311615826..90926f9fa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerHintAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerHintAD.kt @@ -1,10 +1,10 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector.adapter +package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.databinding.ItemEmptyHintBinding import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.scrobbling.ui.selector.model.ScrobblerHint +import org.koitharu.kotatsu.scrobbling.common.ui.selector.model.ScrobblerHint import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.setTextAndVisible import org.koitharu.kotatsu.utils.ext.textAndVisible diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt similarity index 90% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt index ced1485fa..5b74f913a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerMangaSelectionDecoration.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector.adapter +package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import android.content.Context import android.graphics.Canvas @@ -8,7 +8,7 @@ import android.view.View import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_ID import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga import org.koitharu.kotatsu.utils.ext.getItem class ScrobblerMangaSelectionDecoration(context: Context) : MangaSelectionDecoration(context) { diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerSelectorAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt similarity index 87% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerSelectorAdapter.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt index d0f1d78b0..e3d7af6c6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblerSelectorAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector.adapter +package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.DiffUtil @@ -9,8 +9,8 @@ import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.ui.selector.model.ScrobblerHint +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.ui.selector.model.ScrobblerHint import kotlin.jvm.internal.Intrinsics class ScrobblerSelectorAdapter( diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblingMangaAD.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt similarity index 90% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblingMangaAD.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt index 73723229a..d56427fbd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/adapter/ScrobblingMangaAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector.adapter +package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import androidx.lifecycle.LifecycleOwner import coil.ImageLoader @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.ItemMangaListBinding import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/model/ScrobblerHint.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/model/ScrobblerHint.kt similarity index 93% rename from app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/model/ScrobblerHint.kt rename to app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/model/ScrobblerHint.kt index e30614da2..83c122fa7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ui/selector/model/ScrobblerHint.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/model/ScrobblerHint.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.scrobbling.ui.selector.model +package org.koitharu.kotatsu.scrobbling.common.ui.selector.model import androidx.annotation.DrawableRes import androidx.annotation.StringRes diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingStatus.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingStatus.kt deleted file mode 100644 index cfb408094..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/domain/model/ScrobblingStatus.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.domain.model - -enum class ScrobblingStatus { - - PLANNED, - READING, - RE_READING, - COMPLETED, - ON_HOLD, - DROPPED, -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt index c0c650c0a..0e43a2e6b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt @@ -7,9 +7,9 @@ import okhttp3.Response import okhttp3.Route import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType import javax.inject.Inject import javax.inject.Provider diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt index e7d28b261..f023d5f75 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.scrobbling.mal.data import okhttp3.Interceptor import okhttp3.Response import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage private const val JSON = "application/json" diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt index f6adf275d..b4f026298 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt @@ -9,20 +9,18 @@ import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.util.await -import org.koitharu.kotatsu.parsers.util.json.mapJSON +import org.koitharu.kotatsu.parsers.util.json.mapJSONNotNull import org.koitharu.kotatsu.parsers.util.parseJson -import org.koitharu.kotatsu.parsers.util.toIntUp -import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser import org.koitharu.kotatsu.utils.PKCEGenerator private const val REDIRECT_URI = "kotatsu://mal-auth" -private const val BASE_OAUTH_URL = "https://myanimelist.net" +private const val BASE_WEB_URL = "https://myanimelist.net" private const val BASE_API_URL = "https://api.myanimelist.net/v2" private const val AVATAR_STUB = "https://cdn.myanimelist.net/images/questionmark_50.gif" @@ -30,12 +28,12 @@ class MALRepository( private val okHttp: OkHttpClient, private val storage: ScrobblerStorage, private val db: MangaDatabase, -) : ScrobblerRepository { +) : org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository { private var codeVerifier: String = getPKCEChallengeCode() override val oauthUrl: String - get() = "$BASE_OAUTH_URL/v1/oauth2/authorize?" + + get() = "$BASE_WEB_URL/v1/oauth2/authorize?" + "response_type=code" + "&client_id=${BuildConfig.MAL_CLIENT_ID}" + "&redirect_uri=$REDIRECT_URI" + @@ -61,7 +59,7 @@ class MALRepository( } val request = Request.Builder() .post(body.build()) - .url("${BASE_OAUTH_URL}/v1/oauth2/token") + .url("${BASE_WEB_URL}/v1/oauth2/token") val response = okHttp.newCall(request.build()).await().parseJson() storage.accessToken = response.getString("access_token") @@ -83,17 +81,15 @@ class MALRepository( override suspend fun findManga(query: String, offset: Int): List { val url = BASE_API_URL.toHttpUrl().newBuilder() .addPathSegment("manga") - .addQueryParameter("offset", offset.toFloat().toIntUp().toString()) + .addQueryParameter("offset", offset.toString()) .addQueryParameter("nsfw", "true") - .addQueryParameter( - "q", - query.take(64) - ) // WARNING! MAL API throws a 400 when the query is over 64 characters + // WARNING! MAL API throws a 400 when the query is over 64 characters + .addQueryParameter("q", query.take(64)) .build() val request = Request.Builder().url(url).get().build() val response = okHttp.newCall(request).await().parseJson() val data = response.getJSONArray("data") - return data.mapJSON { jsonToManga(it) } + return data.mapJSONNotNull { jsonToManga(it) } } override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { @@ -181,7 +177,7 @@ class MALRepository( return codeVerifier } - private fun jsonToManga(json: JSONObject): ScrobblerManga { + private fun jsonToManga(json: JSONObject): ScrobblerManga? { for (i in 0 until json.length()) { val node = json.getJSONObject("node") return ScrobblerManga( @@ -189,23 +185,17 @@ class MALRepository( name = node.getString("title"), altName = null, cover = node.getJSONObject("main_picture").getString("large"), - url = "https://myanimelist.net/manga/${node.getLong("id")}" + url = "$BASE_WEB_URL/manga/${node.getLong("id")}", ) } - return ScrobblerManga( - id = 1, - name = "", - altName = null, - cover = "", - url = "" - ) + return null } private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo( id = json.getLong("id"), name = json.getString("title"), cover = json.getJSONObject("main_picture").getString("large"), - url = "https://myanimelist.net/manga/${json.getLong("id")}", + url = "$BASE_WEB_URL/manga/${json.getLong("id")}", descriptionHtml = json.getString("synopsis"), ) diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt index 7902865fb..ce5d040ee 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt @@ -1,12 +1,9 @@ package org.koitharu.kotatsu.scrobbling.mal.domain import org.koitharu.kotatsu.core.db.MangaDatabase -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt deleted file mode 100644 index ec0b185cd..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.mal.ui - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.preference.Preference -import coil.ImageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation -import dagger.hilt.android.AndroidEntryPoint -import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.base.ui.BasePreferenceFragment -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser -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 -import javax.inject.Inject - -@AndroidEntryPoint -class MALSettingsFragment : BasePreferenceFragment(R.string.mal) { - - @Inject - lateinit var coil: ImageLoader - - @Inject - lateinit var viewModelFactory: MALSettingsViewModel.Factory - - private val viewModel by assistedViewModels { - viewModelFactory.create(arguments?.getString(ARG_AUTH_CODE)) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.pref_mal) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.user.observe(viewLifecycleOwner, this::onUserChanged) - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - return when (preference.key) { - KEY_USER -> openAuthorization() - KEY_LOGOUT -> { - viewModel.logout() - true - } - else -> super.onPreferenceTreeClick(preference) - } - } - - private fun onUserChanged(user: ScrobblerUser?) { - val pref = findPreference(KEY_USER) ?: return - pref.isSelectable = user == null - pref.title = user?.nickname ?: getString(R.string.sign_in) - ImageRequest.Builder(requireContext()) - .data(user?.avatar) - .transformations(CircleCropTransformation()) - .target(PreferenceIconTarget(pref)) - .enqueueWith(coil) - findPreference(KEY_LOGOUT)?.isVisible = user != null - } - - private fun openAuthorization(): Boolean { - return runCatching { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(viewModel.authorizationUrl) - startActivity(intent) - }.isSuccess - } - - companion object { - - private const val KEY_USER = "mal_user" - private const val KEY_LOGOUT = "mal_logout" - - private const val ARG_AUTH_CODE = "auth_code" - - fun newInstance(authCode: String?) = MALSettingsFragment().withArgs(1) { - putString(ARG_AUTH_CODE, authCode) - } - } -} - diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt deleted file mode 100644 index 709837678..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.mal.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.domain.model.ScrobblerUser -import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository - -class MALSettingsViewModel @AssistedInject constructor( - private val repository: MALRepository, - @Assisted authCode: String?, -) : BaseViewModel() { - - val authorizationUrl: String - get() = repository.oauthUrl - - val user = MutableLiveData() - - init { - if (authCode != null) { - authorize(authCode) - } else { - loadUser() - } - } - - fun logout() { - launchJob(Dispatchers.Default) { - repository.logout() - user.postValue(null) - } - } - - private fun loadUser() = launchJob(Dispatchers.Default) { - val userModel = if (repository.isAuthorized) { - repository.cachedUser?.let(user::postValue) - repository.loadUser() - } else { - null - } - user.postValue(userModel) - } - - private fun authorize(code: String) = launchJob(Dispatchers.Default) { - repository.authorize(code) - user.postValue(repository.loadUser()) - } - - @AssistedFactory - interface Factory { - - fun create(authCode: String?): MALSettingsViewModel - } -} 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 dbaad2cc7..52b3f9550 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 @@ -7,9 +7,9 @@ import okhttp3.Response import okhttp3.Route import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType import javax.inject.Inject import javax.inject.Provider diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriInterceptor.kt index 466382960..dfd6e93aa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriInterceptor.kt @@ -4,7 +4,7 @@ import okhttp3.Interceptor import okhttp3.Response import okio.IOException import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage private const val USER_AGENT_SHIKIMORI = "Kotatsu" 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 5b86c937b..ecee1125a 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 @@ -14,13 +14,13 @@ import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.parseJson import org.koitharu.kotatsu.parsers.util.parseJsonArray import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl -import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository -import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser import org.koitharu.kotatsu.utils.ext.toRequestBody private const val REDIRECT_URI = "kotatsu://shikimori-auth" 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 5c73c0532..e7a06d53c 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,9 +1,9 @@ package org.koitharu.kotatsu.scrobbling.shikimori.domain import org.koitharu.kotatsu.core.db.MangaDatabase -import org.koitharu.kotatsu.scrobbling.domain.Scrobbler -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus +import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository import javax.inject.Inject import javax.inject.Singleton 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 deleted file mode 100644 index da45cf4ed..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.shikimori.ui - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.preference.Preference -import coil.ImageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation -import dagger.hilt.android.AndroidEntryPoint -import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.base.ui.BasePreferenceFragment -import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser -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 -import javax.inject.Inject - -@AndroidEntryPoint -class ShikimoriSettingsFragment : BasePreferenceFragment(R.string.shikimori) { - - @Inject - lateinit var coil: ImageLoader - - @Inject - lateinit var viewModelFactory: ShikimoriSettingsViewModel.Factory - - private val viewModel by assistedViewModels { - viewModelFactory.create(arguments?.getString(ARG_AUTH_CODE)) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.pref_shikimori) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.user.observe(viewLifecycleOwner, this::onUserChanged) - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - return when (preference.key) { - KEY_USER -> openAuthorization() - KEY_LOGOUT -> { - viewModel.logout() - true - } - - else -> super.onPreferenceTreeClick(preference) - } - } - - private fun onUserChanged(user: ScrobblerUser?) { - val pref = findPreference(KEY_USER) ?: return - pref.isSelectable = user == null - pref.title = user?.nickname ?: getString(R.string.sign_in) - ImageRequest.Builder(requireContext()) - .data(user?.avatar) - .transformations(CircleCropTransformation()) - .target(PreferenceIconTarget(pref)) - .enqueueWith(coil) - findPreference(KEY_LOGOUT)?.isVisible = user != null - } - - private fun openAuthorization(): Boolean { - return runCatching { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(viewModel.authorizationUrl) - startActivity(intent) - }.isSuccess - } - - companion object { - - private const val KEY_USER = "shiki_user" - private const val KEY_LOGOUT = "shiki_logout" - - private const val ARG_AUTH_CODE = "auth_code" - - fun newInstance(authCode: String?) = ShikimoriSettingsFragment().withArgs(1) { - putString(ARG_AUTH_CODE, authCode) - } - } -} 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 deleted file mode 100644 index 2e689e673..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/shikimori/ui/ShikimoriSettingsViewModel.kt +++ /dev/null @@ -1,57 +0,0 @@ -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.domain.model.ScrobblerUser -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository - -class ShikimoriSettingsViewModel @AssistedInject constructor( - private val repository: ShikimoriRepository, - @Assisted authCode: String?, -) : BaseViewModel() { - - val authorizationUrl: String - get() = repository.oauthUrl - - val user = MutableLiveData() - - init { - if (authCode != null) { - authorize(authCode) - } else { - loadUser() - } - } - - fun logout() { - launchJob(Dispatchers.Default) { - repository.logout() - user.postValue(null) - } - } - - private fun loadUser() = launchJob(Dispatchers.Default) { - val userModel = if (repository.isAuthorized) { - repository.cachedUser?.let(user::postValue) - repository.loadUser() - } else { - null - } - user.postValue(userModel) - } - - private fun authorize(code: String) = launchJob(Dispatchers.Default) { - repository.authorize(code) - user.postValue(repository.loadUser()) - } - - @AssistedFactory - interface Factory { - - fun create(authCode: String?): ShikimoriSettingsViewModel - } -} 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 beccdf6f5..7833095f4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt @@ -20,7 +20,8 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.scrobbling.anilist.data.AniListRepository -import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.ui.config.ScrobblerConfigActivity import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository import org.koitharu.kotatsu.search.domain.MangaSearchRepository @@ -130,28 +131,28 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach AppSettings.KEY_SHIKIMORI -> { if (!shikimoriRepository.isAuthorized) { launchScrobblerAuth(shikimoriRepository) - true } else { - super.onPreferenceTreeClick(preference) + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.SHIKIMORI)) } + true } AppSettings.KEY_MAL -> { if (!malRepository.isAuthorized) { launchScrobblerAuth(malRepository) - true } else { - super.onPreferenceTreeClick(preference) + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.MAL)) } + true } AppSettings.KEY_ANILIST -> { if (!aniListRepository.isAuthorized) { launchScrobblerAuth(aniListRepository) - true } else { - super.onPreferenceTreeClick(preference) + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.ANILIST)) } + true } else -> super.onPreferenceTreeClick(preference) @@ -217,7 +218,10 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach }.show() } - private fun bindScrobblerSummary(key: String, repository: ScrobblerRepository) { + private fun bindScrobblerSummary( + key: String, + repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository + ) { val pref = findPreference(key) ?: return if (!repository.isAuthorized) { pref.setSummary(R.string.disabled) @@ -242,7 +246,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach } } - private fun launchScrobblerAuth(repository: ScrobblerRepository) { + private fun launchScrobblerAuth(repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository) { runCatching { val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse(repository.oauthUrl) 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 21bea36ed..eadf48103 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -3,7 +3,6 @@ package org.koitharu.kotatsu.settings import android.content.ComponentName import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -24,9 +23,6 @@ import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.scrobbling.anilist.ui.AniListSettingsFragment -import org.koitharu.kotatsu.scrobbling.mal.ui.MALSettingsFragment -import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment import org.koitharu.kotatsu.utils.ext.isScrolledToTop @@ -126,10 +122,8 @@ class SettingsActivity : private fun openDefaultFragment() { val fragment = when (intent?.action) { - Intent.ACTION_VIEW -> handleUri(intent.data) ?: return ACTION_READER -> ReaderSettingsFragment() ACTION_SUGGESTIONS -> SuggestionsSettingsFragment() - ACTION_SHIKIMORI -> ShikimoriSettingsFragment() ACTION_HISTORY -> HistorySettingsFragment() ACTION_TRACKER -> TrackerSettingsFragment() ACTION_SOURCE -> SourceSettingsFragment.newInstance( @@ -145,21 +139,6 @@ class SettingsActivity : } } - private fun handleUri(uri: Uri?): Fragment? { - when (uri?.host) { - HOST_SHIKIMORI_AUTH -> - return ShikimoriSettingsFragment.newInstance(authCode = uri.getQueryParameter("code")) - - HOST_ANILIST_AUTH -> - return AniListSettingsFragment.newInstance(authCode = uri.getQueryParameter("code")) - - HOST_MAL_AUTH -> - return MALSettingsFragment.newInstance(authCode = uri.getQueryParameter("code")) - } - finishAfterTransition() - return null - } - companion object { private const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS" @@ -171,20 +150,12 @@ class SettingsActivity : private const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST" private const val EXTRA_SOURCE = "source" - private const val HOST_SHIKIMORI_AUTH = "shikimori-auth" - private const val HOST_ANILIST_AUTH = "anilist-auth" - private const val HOST_MAL_AUTH = "mal-auth" - fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) fun newReaderSettingsIntent(context: Context) = Intent(context, SettingsActivity::class.java) .setAction(ACTION_READER) - fun newShikimoriSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_SHIKIMORI) - fun newSuggestionsSettingsIntent(context: Context) = Intent(context, SettingsActivity::class.java) .setAction(ACTION_SUGGESTIONS) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt index f4e7ed794..95b51d35a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt @@ -41,8 +41,6 @@ class FeedFragment : private val viewModel by viewModels() private var feedAdapter: FeedAdapter? = null - private var paddingVertical = 0 - private var paddingHorizontal = 0 override fun onInflateView( inflater: LayoutInflater, @@ -57,8 +55,6 @@ class FeedFragment : setHasFixedSize(true) addOnScrollListener(PaginationScrollListener(4, this@FeedFragment)) val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing) - paddingHorizontal = spacing - paddingVertical = resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer) val decoration = TypedSpacingItemDecoration( FeedAdapter.ITEM_TYPE_FEED to 0, fallbackSpacing = spacing, diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/FlowExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/FlowExt.kt index 03ac09a19..4c08eec67 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/FlowExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/FlowExt.kt @@ -2,7 +2,12 @@ package org.koitharu.kotatsu.utils.ext import android.os.SystemClock import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.transformLatest fun Flow.onFirst(action: suspend (T) -> Unit): Flow { var isFirstCall = true @@ -11,6 +16,8 @@ fun Flow.onFirst(action: suspend (T) -> Unit): Flow { action(it) isFirstCall = false } + }.onCompletion { + isFirstCall = true } } diff --git a/app/src/main/res/drawable-hdpi/ic_shikimori.png b/app/src/main/res/drawable-hdpi/ic_shikimori_raw.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_shikimori.png rename to app/src/main/res/drawable-hdpi/ic_shikimori_raw.png diff --git a/app/src/main/res/drawable-mdpi/ic_shikimori.png b/app/src/main/res/drawable-mdpi/ic_shikimori_raw.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_shikimori.png rename to app/src/main/res/drawable-mdpi/ic_shikimori_raw.png diff --git a/app/src/main/res/drawable-xhdpi/ic_shikimori.png b/app/src/main/res/drawable-xhdpi/ic_shikimori_raw.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_shikimori.png rename to app/src/main/res/drawable-xhdpi/ic_shikimori_raw.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_shikimori.png b/app/src/main/res/drawable-xxhdpi/ic_shikimori_raw.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_shikimori.png rename to app/src/main/res/drawable-xxhdpi/ic_shikimori_raw.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shikimori.png b/app/src/main/res/drawable-xxxhdpi/ic_shikimori_raw.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_shikimori.png rename to app/src/main/res/drawable-xxxhdpi/ic_shikimori_raw.png diff --git a/app/src/main/res/drawable/ic_anilist.xml b/app/src/main/res/drawable/ic_anilist.xml index 3be2a1500..88a718884 100644 --- a/app/src/main/res/drawable/ic_anilist.xml +++ b/app/src/main/res/drawable/ic_anilist.xml @@ -2,6 +2,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" + android:tint="?colorControlNormal" android:viewportWidth="18" android:viewportHeight="18"> + diff --git a/app/src/main/res/layout/activity_scrobbler_config.xml b/app/src/main/res/layout/activity_scrobbler_config.xml new file mode 100644 index 000000000..1bdc1a44d --- /dev/null +++ b/app/src/main/res/layout/activity_scrobbler_config.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_scrobbling_manga.xml b/app/src/main/res/layout/item_scrobbling_manga.xml new file mode 100644 index 000000000..512ca35e9 --- /dev/null +++ b/app/src/main/res/layout/item_scrobbling_manga.xml @@ -0,0 +1,51 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0aab41752..704426500 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -416,4 +416,6 @@ Mion Rikka Sakura + There is nothing here + To track reading progress, select Menu → Track on the manga details screen. diff --git a/app/src/main/res/xml/pref_anilist.xml b/app/src/main/res/xml/pref_anilist.xml deleted file mode 100644 index 7cd04cada..000000000 --- a/app/src/main/res/xml/pref_anilist.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/xml/pref_history.xml b/app/src/main/res/xml/pref_history.xml index b0975f4c5..2aafbb923 100644 --- a/app/src/main/res/xml/pref_history.xml +++ b/app/src/main/res/xml/pref_history.xml @@ -22,23 +22,20 @@ - - - + android:title="@string/shikimori" + app:icon="@drawable/ic_shikimori" /> - + android:title="@string/anilist" + app:icon="@drawable/ic_anilist" /> + + diff --git a/app/src/main/res/xml/pref_mal.xml b/app/src/main/res/xml/pref_mal.xml deleted file mode 100644 index 67a9fb806..000000000 --- a/app/src/main/res/xml/pref_mal.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/xml/pref_shikimori.xml b/app/src/main/res/xml/pref_shikimori.xml deleted file mode 100644 index 0e8d9118f..000000000 --- a/app/src/main/res/xml/pref_shikimori.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - -