diff --git a/app/build.gradle b/app/build.gradle index 8a46dbe22..729156f62 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,7 +94,7 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.10' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' - implementation "androidx.appcompat:appcompat:1.6.0" + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.activity:activity-ktx:1.6.1' implementation 'androidx.fragment:fragment-ktx:1.5.5' diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt index f8aa998d8..61da14d36 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt @@ -1,17 +1,12 @@ package org.koitharu.kotatsu.settings -import android.accounts.AccountManager -import android.content.ActivityNotFoundException import android.content.SharedPreferences import android.os.Bundle import android.view.View import androidx.preference.ListPreference import androidx.preference.Preference -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog @@ -21,7 +16,6 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.parsers.util.names import org.koitharu.kotatsu.settings.utils.SliderPreference -import org.koitharu.kotatsu.sync.ui.SyncSettingsIntent import org.koitharu.kotatsu.utils.ext.getStorageName import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat import org.koitharu.kotatsu.utils.ext.viewLifecycleScope @@ -71,11 +65,6 @@ class ContentSettingsFragment : settings.subscribe(this) } - override fun onResume() { - super.onResume() - bindSyncSummary() - } - override fun onDestroyView() { settings.unsubscribe(this) super.onDestroyView() @@ -111,22 +100,6 @@ class ContentSettingsFragment : true } - AppSettings.KEY_SYNC -> { - val am = AccountManager.get(requireContext()) - val accountType = getString(R.string.account_type_sync) - val account = am.getAccountsByType(accountType).firstOrNull() - if (account == null) { - am.addAccount(accountType, accountType, null, null, requireActivity(), null, null) - } else { - try { - startActivity(SyncSettingsIntent(account)) - } catch (_: ActivityNotFoundException) { - Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() - } - } - true - } - else -> super.onPreferenceTreeClick(preference) } } @@ -148,16 +121,4 @@ class ContentSettingsFragment : summary = getString(R.string.enabled_d_of_d, total - settings.hiddenSources.size, total) } } - - private fun bindSyncSummary() { - viewLifecycleScope.launch { - val account = withContext(Dispatchers.Default) { - val type = getString(R.string.account_type_sync) - AccountManager.get(requireContext()).getAccountsByType(type).firstOrNull() - } - findPreference(AppSettings.KEY_SYNC)?.run { - summary = account?.name ?: getString(R.string.sync_title) - } - } - } } 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 7833095f4..cc0cf7cf4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.settings -import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.View import androidx.preference.Preference @@ -9,9 +7,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar @@ -19,16 +15,10 @@ import org.koitharu.kotatsu.core.os.ShortcutsUpdater 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.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 import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.ext.getDisplayMessage -import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.viewLifecycleScope import javax.inject.Inject @@ -44,15 +34,6 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach @Inject lateinit var storageManager: LocalStorageManager - @Inject - lateinit var shikimoriRepository: ShikimoriRepository - - @Inject - lateinit var aniListRepository: AniListRepository - - @Inject - lateinit var malRepository: MALRepository - @Inject lateinit var cookieJar: MutableCookieJar @@ -85,13 +66,6 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach } } - override fun onResume() { - super.onResume() - bindScrobblerSummary(AppSettings.KEY_SHIKIMORI, shikimoriRepository) - bindScrobblerSummary(AppSettings.KEY_ANILIST, aniListRepository) - bindScrobblerSummary(AppSettings.KEY_MAL, malRepository) - } - override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { AppSettings.KEY_PAGES_CACHE_CLEAR -> { @@ -128,33 +102,6 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach true } - AppSettings.KEY_SHIKIMORI -> { - if (!shikimoriRepository.isAuthorized) { - launchScrobblerAuth(shikimoriRepository) - } else { - startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.SHIKIMORI)) - } - true - } - - AppSettings.KEY_MAL -> { - if (!malRepository.isAuthorized) { - launchScrobblerAuth(malRepository) - } else { - startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.MAL)) - } - true - } - - AppSettings.KEY_ANILIST -> { - if (!aniListRepository.isAuthorized) { - launchScrobblerAuth(aniListRepository) - } else { - startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.ANILIST)) - } - true - } - else -> super.onPreferenceTreeClick(preference) } } @@ -217,42 +164,4 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach } }.show() } - - 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) - return - } - val username = repository.cachedUser?.nickname - if (username != null) { - pref.summary = getString(R.string.logged_in_as, username) - } else { - pref.setSummary(R.string.loading_) - viewLifecycleScope.launch { - pref.summary = withContext(Dispatchers.Default) { - runCatching { - val user = repository.loadUser() - getString(R.string.logged_in_as, user.nickname) - }.getOrElse { - it.printStackTraceDebug() - it.getDisplayMessage(resources) - } - } - } - } - } - - private fun launchScrobblerAuth(repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository) { - runCatching { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(repository.oauthUrl) - startActivity(intent) - }.onFailure { - Snackbar.make(listView, it.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show() - } - } } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt new file mode 100644 index 000000000..24e5c97bb --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt @@ -0,0 +1,150 @@ +package org.koitharu.kotatsu.settings + +import android.accounts.AccountManager +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BasePreferenceFragment +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.scrobbling.anilist.data.AniListRepository +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.sync.ui.SyncSettingsIntent +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.printStackTraceDebug +import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +import javax.inject.Inject + +@AndroidEntryPoint +class ServicesSettingsFragment : BasePreferenceFragment(R.string.services) { + + @Inject + lateinit var shikimoriRepository: ShikimoriRepository + + @Inject + lateinit var aniListRepository: AniListRepository + + @Inject + lateinit var malRepository: MALRepository + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.pref_services) + } + + override fun onResume() { + super.onResume() + bindScrobblerSummary(AppSettings.KEY_SHIKIMORI, shikimoriRepository) + bindScrobblerSummary(AppSettings.KEY_ANILIST, aniListRepository) + bindScrobblerSummary(AppSettings.KEY_MAL, malRepository) + bindSyncSummary() + } + + override fun onPreferenceTreeClick(preference: Preference): Boolean { + return when (preference.key) { + AppSettings.KEY_SHIKIMORI -> { + if (!shikimoriRepository.isAuthorized) { + launchScrobblerAuth(shikimoriRepository) + } else { + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.SHIKIMORI)) + } + true + } + + AppSettings.KEY_MAL -> { + if (!malRepository.isAuthorized) { + launchScrobblerAuth(malRepository) + } else { + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.MAL)) + } + true + } + + AppSettings.KEY_ANILIST -> { + if (!aniListRepository.isAuthorized) { + launchScrobblerAuth(aniListRepository) + } else { + startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.ANILIST)) + } + true + } + + AppSettings.KEY_SYNC -> { + val am = AccountManager.get(requireContext()) + val accountType = getString(R.string.account_type_sync) + val account = am.getAccountsByType(accountType).firstOrNull() + if (account == null) { + am.addAccount(accountType, accountType, null, null, requireActivity(), null, null) + } else { + try { + startActivity(SyncSettingsIntent(account)) + } catch (_: ActivityNotFoundException) { + Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + } + } + true + } + + else -> super.onPreferenceTreeClick(preference) + } + } + + 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) + return + } + val username = repository.cachedUser?.nickname + if (username != null) { + pref.summary = getString(R.string.logged_in_as, username) + } else { + pref.setSummary(R.string.loading_) + viewLifecycleScope.launch { + pref.summary = withContext(Dispatchers.Default) { + runCatching { + val user = repository.loadUser() + getString(R.string.logged_in_as, user.nickname) + }.getOrElse { + it.printStackTraceDebug() + it.getDisplayMessage(resources) + } + } + } + } + } + + private fun launchScrobblerAuth(repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository) { + runCatching { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(repository.oauthUrl) + startActivity(intent) + }.onFailure { + Snackbar.make(listView, it.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show() + } + } + + private fun bindSyncSummary() { + viewLifecycleScope.launch { + val account = withContext(Dispatchers.Default) { + val type = getString(R.string.account_type_sync) + AccountManager.get(requireContext()).getAccountsByType(type).firstOrNull() + } + findPreference(AppSettings.KEY_SYNC)?.run { + summary = account?.name ?: getString(R.string.sync_title) + } + } + } +} diff --git a/app/src/main/res/drawable/ic_services.xml b/app/src/main/res/drawable/ic_services.xml new file mode 100644 index 000000000..fe6404dbc --- /dev/null +++ b/app/src/main/res/drawable/ic_services.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index aee18b1e6..3b0616df6 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -13,9 +13,8 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 704426500..2e2e1181e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -418,4 +418,5 @@ Sakura There is nothing here To track reading progress, select Menu → Track on the manga details screen. + Services diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index f24d9adc6..82987a41d 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -73,6 +73,7 @@ @style/Widget.Kotatsu.ListItemTextView @style/Widget.Material3.CompoundButton.MaterialSwitch @style/Preference.SwitchPreferenceCompat.M3 + @style/Widget.Material3.CollapsingToolbar.Medium @style/TextAppearance.Kotatsu.Menu diff --git a/app/src/main/res/xml/pref_appearance.xml b/app/src/main/res/xml/pref_appearance.xml index fa5236c23..5f2058cee 100644 --- a/app/src/main/res/xml/pref_appearance.xml +++ b/app/src/main/res/xml/pref_appearance.xml @@ -22,25 +22,35 @@ android:summary="@string/black_dark_theme_summary" android:title="@string/black_dark_theme" /> + + + + + + + + + + - - - - - - + android:title="@string/backup_restore" + app:allowDividerAbove="true" /> diff --git a/app/src/main/res/xml/pref_history.xml b/app/src/main/res/xml/pref_history.xml index 2aafbb923..a7304418c 100644 --- a/app/src/main/res/xml/pref_history.xml +++ b/app/src/main/res/xml/pref_history.xml @@ -1,13 +1,6 @@ - - + xmlns:android="http://schemas.android.com/apk/res/android"> - - - - - - - - - - + + + + + + + + + + + + + + + + +