Suggestions settings
This commit is contained in:
@@ -135,6 +135,12 @@ class AppSettings(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isSuggestionsEnabled: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_SUGGESTIONS, false)
|
||||||
|
|
||||||
|
val isSuggestionsExcludeNsfw: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_SUGGESTIONS_EXCLUDE_NSFW, false)
|
||||||
|
|
||||||
fun getDateFormat(format: String = prefs.getString(KEY_DATE_FORMAT, "").orEmpty()): DateFormat =
|
fun getDateFormat(format: String = prefs.getString(KEY_DATE_FORMAT, "").orEmpty()): DateFormat =
|
||||||
when (format) {
|
when (format) {
|
||||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||||
@@ -224,6 +230,8 @@ class AppSettings(context: Context) {
|
|||||||
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
||||||
const val KEY_HISTORY_EXCLUDE_NSFW = "history_exclude_nsfw"
|
const val KEY_HISTORY_EXCLUDE_NSFW = "history_exclude_nsfw"
|
||||||
const val KEY_PAGES_NUMBERS = "pages_numbers"
|
const val KEY_PAGES_NUMBERS = "pages_numbers"
|
||||||
|
const val KEY_SUGGESTIONS = "suggestions"
|
||||||
|
const val KEY_SUGGESTIONS_EXCLUDE_NSFW = "suggestions_exclude_nsfw"
|
||||||
|
|
||||||
// About
|
// About
|
||||||
const val KEY_APP_UPDATE = "app_update"
|
const val KEY_APP_UPDATE = "app_update"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class HistoryRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getAllTags(): List<MangaTag> {
|
suspend fun getAllTags(): Set<MangaTag> {
|
||||||
return db.historyDao.findAllTags().map { x -> x.toMangaTag() }
|
return db.historyDao.findAllTags().mapToSet { x -> x.toMangaTag() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,12 +43,15 @@ import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
|
|||||||
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
|
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
|
||||||
import org.koitharu.kotatsu.settings.AppUpdateChecker
|
import org.koitharu.kotatsu.settings.AppUpdateChecker
|
||||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment
|
||||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment
|
import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment
|
||||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||||
import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment
|
|
||||||
import org.koitharu.kotatsu.tracker.ui.FeedFragment
|
import org.koitharu.kotatsu.tracker.ui.FeedFragment
|
||||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
import org.koitharu.kotatsu.utils.ext.hideKeyboard
|
||||||
|
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||||
|
import org.koitharu.kotatsu.utils.ext.resolveDp
|
||||||
|
|
||||||
class MainActivity : BaseActivity<ActivityMainBinding>(),
|
class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||||
NavigationView.OnNavigationItemSelectedListener, AppBarOwner,
|
NavigationView.OnNavigationItemSelectedListener, AppBarOwner,
|
||||||
@@ -116,6 +119,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
|||||||
viewModel.onError.observe(this, this::onError)
|
viewModel.onError.observe(this, this::onError)
|
||||||
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
||||||
viewModel.remoteSources.observe(this, this::updateSideMenu)
|
viewModel.remoteSources.observe(this, this::updateSideMenu)
|
||||||
|
viewModel.isSuggestionsEnabled.observe(this, this::setSuggestionsEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
@@ -301,6 +305,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
|||||||
submenu.setGroupCheckable(R.id.group_remote_sources, true, true)
|
submenu.setGroupCheckable(R.id.group_remote_sources, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setSuggestionsEnabled(isEnabled: Boolean) {
|
||||||
|
val item = binding.navigationView.menu.findItem(R.id.nav_suggestions) ?: return
|
||||||
|
if (!isEnabled && item.isChecked) {
|
||||||
|
binding.navigationView.setCheckedItem(R.id.nav_history)
|
||||||
|
}
|
||||||
|
item.isVisible = isEnabled
|
||||||
|
}
|
||||||
|
|
||||||
private fun openDefaultSection() {
|
private fun openDefaultSection() {
|
||||||
when (viewModel.defaultSection) {
|
when (viewModel.defaultSection) {
|
||||||
AppSection.LOCAL -> {
|
AppSection.LOCAL -> {
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ class MainViewModel(
|
|||||||
val onOpenReader = SingleLiveEvent<Manga>()
|
val onOpenReader = SingleLiveEvent<Manga>()
|
||||||
var defaultSection by settings::defaultSection
|
var defaultSection by settings::defaultSection
|
||||||
|
|
||||||
|
val isSuggestionsEnabled = settings.observe()
|
||||||
|
.filter { it == AppSettings.KEY_SUGGESTIONS }
|
||||||
|
.onStart { emit("") }
|
||||||
|
.map { settings.isSuggestionsEnabled }
|
||||||
|
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||||
|
|
||||||
val remoteSources = settings.observe()
|
val remoteSources = settings.observe()
|
||||||
.filter { it == AppSettings.KEY_SOURCES_ORDER || it == AppSettings.KEY_SOURCES_HIDDEN }
|
.filter { it == AppSettings.KEY_SOURCES_ORDER || it == AppSettings.KEY_SOURCES_HIDDEN }
|
||||||
.onStart { emit("") }
|
.onStart { emit("") }
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.TwoStatePreference
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import leakcanary.LeakCanary
|
import leakcanary.LeakCanary
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
@@ -56,7 +56,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
|||||||
entryValues = ListMode.values().names()
|
entryValues = ListMode.values().names()
|
||||||
setDefaultValueCompat(ListMode.GRID.name)
|
setDefaultValueCompat(ListMode.GRID.name)
|
||||||
}
|
}
|
||||||
findPreference<SwitchPreference>(AppSettings.KEY_DYNAMIC_THEME)?.isVisible =
|
findPreference<Preference>(AppSettings.KEY_DYNAMIC_THEME)?.isVisible =
|
||||||
AppSettings.isDynamicColorAvailable
|
AppSettings.isDynamicColorAvailable
|
||||||
findPreference<ListPreference>(AppSettings.KEY_DATE_FORMAT)?.run {
|
findPreference<ListPreference>(AppSettings.KEY_DATE_FORMAT)?.run {
|
||||||
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
|
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
|
||||||
@@ -72,12 +72,15 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
|||||||
setDefaultValueCompat("")
|
setDefaultValueCompat("")
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
}
|
}
|
||||||
|
findPreference<Preference>(AppSettings.KEY_SUGGESTIONS)?.setSummary(
|
||||||
|
if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
findPreference<Preference>(AppSettings.KEY_LOCAL_STORAGE)?.bindStorageName()
|
findPreference<Preference>(AppSettings.KEY_LOCAL_STORAGE)?.bindStorageName()
|
||||||
findPreference<SwitchPreference>(AppSettings.KEY_PROTECT_APP)?.isChecked =
|
findPreference<TwoStatePreference>(AppSettings.KEY_PROTECT_APP)?.isChecked =
|
||||||
!settings.appPassword.isNullOrEmpty()
|
!settings.appPassword.isNullOrEmpty()
|
||||||
settings.subscribe(this)
|
settings.subscribe(this)
|
||||||
}
|
}
|
||||||
@@ -114,15 +117,20 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
|||||||
findPreference<Preference>(key)?.setSummary(R.string.restart_required)
|
findPreference<Preference>(key)?.setSummary(R.string.restart_required)
|
||||||
}
|
}
|
||||||
AppSettings.KEY_HIDE_TOOLBAR -> {
|
AppSettings.KEY_HIDE_TOOLBAR -> {
|
||||||
findPreference<SwitchPreference>(key)?.setSummary(R.string.restart_required)
|
findPreference<Preference>(key)?.setSummary(R.string.restart_required)
|
||||||
}
|
}
|
||||||
AppSettings.KEY_LOCAL_STORAGE -> {
|
AppSettings.KEY_LOCAL_STORAGE -> {
|
||||||
findPreference<Preference>(key)?.bindStorageName()
|
findPreference<Preference>(key)?.bindStorageName()
|
||||||
}
|
}
|
||||||
AppSettings.KEY_APP_PASSWORD -> {
|
AppSettings.KEY_APP_PASSWORD -> {
|
||||||
findPreference<SwitchPreference>(AppSettings.KEY_PROTECT_APP)
|
findPreference<TwoStatePreference>(AppSettings.KEY_PROTECT_APP)
|
||||||
?.isChecked = !settings.appPassword.isNullOrEmpty()
|
?.isChecked = !settings.appPassword.isNullOrEmpty()
|
||||||
}
|
}
|
||||||
|
AppSettings.KEY_SUGGESTIONS -> {
|
||||||
|
findPreference<Preference>(AppSettings.KEY_SUGGESTIONS)?.setSummary(
|
||||||
|
if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +156,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
AppSettings.KEY_PROTECT_APP -> {
|
AppSettings.KEY_PROTECT_APP -> {
|
||||||
val pref = (preference as? SwitchPreference ?: return false)
|
val pref = (preference as? TwoStatePreference ?: return false)
|
||||||
if (pref.isChecked) {
|
if (pref.isChecked) {
|
||||||
pref.isChecked = false
|
pref.isChecked = false
|
||||||
startActivity(Intent(preference.context, ProtectSetupActivity::class.java))
|
startActivity(Intent(preference.context, ProtectSetupActivity::class.java))
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.koitharu.kotatsu.settings
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||||
|
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||||
|
|
||||||
|
class SuggestionsSettingsFragment : BasePreferenceFragment(R.string.suggestions),
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
private val repository by inject<SuggestionRepository>(mode = LazyThreadSafetyMode.NONE)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
settings.subscribe(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
addPreferencesFromResource(R.xml.pref_suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
settings.unsubscribe(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
|
if (key == AppSettings.KEY_SUGGESTIONS && settings.isSuggestionsEnabled) {
|
||||||
|
onSuggestionsEnabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSuggestionsEnabled() {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (repository.isEmpty()) {
|
||||||
|
SuggestionsWorker.startNow(context ?: return@launch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,9 @@ abstract class SuggestionDao {
|
|||||||
@Query("SELECT * FROM suggestions ORDER BY relevance DESC")
|
@Query("SELECT * FROM suggestions ORDER BY relevance DESC")
|
||||||
abstract fun observeAll(): Flow<List<SuggestionWithManga>>
|
abstract fun observeAll(): Flow<List<SuggestionWithManga>>
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(*) FROM suggestions")
|
||||||
|
abstract suspend fun count(): Int
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
abstract suspend fun insert(entity: SuggestionEntity): Long
|
abstract suspend fun insert(entity: SuggestionEntity): Long
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ class SuggestionRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun clear() {
|
||||||
|
db.suggestionDao.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun isEmpty(): Boolean {
|
||||||
|
return db.suggestionDao.count() == 0
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun replace(suggestions: Iterable<MangaSuggestion>) {
|
suspend fun replace(suggestions: Iterable<MangaSuggestion>) {
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
db.suggestionDao.deleteAll()
|
db.suggestionDao.deleteAll()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.list.ui.MangaListFragment
|
|||||||
|
|
||||||
class SuggestionsFragment : MangaListFragment() {
|
class SuggestionsFragment : MangaListFragment() {
|
||||||
|
|
||||||
override val viewModel by viewModel<SuggestionsViewModel>(mode = LazyThreadSafetyMode.NONE)
|
override val viewModel by viewModel<SuggestionsViewModel>()
|
||||||
override val isSwipeRefreshEnabled = false
|
override val isSwipeRefreshEnabled = false
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.koin.core.component.KoinComponent
|
|||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
import org.koitharu.kotatsu.core.model.SortOrder
|
import org.koitharu.kotatsu.core.model.SortOrder
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||||
import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion
|
import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion
|
||||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||||
@@ -18,10 +19,25 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) :
|
|||||||
|
|
||||||
private val suggestionRepository by inject<SuggestionRepository>()
|
private val suggestionRepository by inject<SuggestionRepository>()
|
||||||
private val historyRepository by inject<HistoryRepository>()
|
private val historyRepository by inject<HistoryRepository>()
|
||||||
|
private val appSettings by inject<AppSettings>()
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result = try {
|
||||||
|
val count = doWorkImpl()
|
||||||
|
Result.success(workDataOf(DATA_COUNT to count))
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun doWorkImpl(): Int {
|
||||||
|
if (!appSettings.isSuggestionsEnabled) {
|
||||||
|
suggestionRepository.clear()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
val rawResults = ArrayList<Manga>()
|
val rawResults = ArrayList<Manga>()
|
||||||
val allTags = historyRepository.getAllTags()
|
val allTags = historyRepository.getAllTags()
|
||||||
|
if (allTags.isEmpty()) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
val tagsBySources = allTags.groupBy { x -> x.source }
|
val tagsBySources = allTags.groupBy { x -> x.source }
|
||||||
for ((source, tags) in tagsBySources) {
|
for ((source, tags) in tagsBySources) {
|
||||||
val repo = mangaRepositoryOf(source)
|
val repo = mangaRepositoryOf(source)
|
||||||
@@ -33,23 +49,31 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suggestionRepository.replace(
|
if (appSettings.isSuggestionsExcludeNsfw) {
|
||||||
rawResults.distinctBy { manga ->
|
rawResults.removeAll { it.isNsfw }
|
||||||
manga.id
|
}
|
||||||
}.map { manga ->
|
if (rawResults.isEmpty()) {
|
||||||
val jointTags = manga.tags intersect allTags
|
return 0
|
||||||
MangaSuggestion(
|
}
|
||||||
manga = manga,
|
val suggestions = rawResults.distinctBy { manga ->
|
||||||
relevance = (jointTags.size / manga.tags.size.toDouble()).pow(2.0).toFloat(),
|
manga.id
|
||||||
)
|
}.map { manga ->
|
||||||
}
|
val jointTags = manga.tags intersect allTags
|
||||||
)
|
MangaSuggestion(
|
||||||
return Result.success()
|
manga = manga,
|
||||||
|
relevance = (jointTags.size / manga.tags.size.toDouble()).pow(2.0).toFloat(),
|
||||||
|
)
|
||||||
|
}.sortedBy { it.relevance }.take(LIMIT)
|
||||||
|
suggestionRepository.replace(suggestions)
|
||||||
|
return suggestions.size
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val TAG = "suggestions"
|
private const val TAG = "suggestions"
|
||||||
|
private const val TAG_ONESHOT = "suggestions_oneshot"
|
||||||
|
private const val LIMIT = 140
|
||||||
|
private const val DATA_COUNT = "count"
|
||||||
|
|
||||||
fun setup(context: Context) {
|
fun setup(context: Context) {
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints.Builder()
|
||||||
@@ -64,5 +88,17 @@ class SuggestionsWorker(appContext: Context, params: WorkerParameters) :
|
|||||||
WorkManager.getInstance(context)
|
WorkManager.getInstance(context)
|
||||||
.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, request)
|
.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startNow(context: Context) {
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
.build()
|
||||||
|
val request = OneTimeWorkRequestBuilder<SuggestionsWorker>()
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.addTag(TAG_ONESHOT)
|
||||||
|
.build()
|
||||||
|
WorkManager.getInstance(context)
|
||||||
|
.enqueue(request)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,6 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
|||||||
import org.koitharu.kotatsu.utils.PendingIntentCompat
|
import org.koitharu.kotatsu.utils.PendingIntentCompat
|
||||||
import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf
|
import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf
|
||||||
import org.koitharu.kotatsu.utils.ext.toBitmapOrNull
|
import org.koitharu.kotatsu.utils.ext.toBitmapOrNull
|
||||||
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
|
||||||
import org.koitharu.kotatsu.utils.progress.Progress
|
import org.koitharu.kotatsu.utils.progress.Progress
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@@ -237,6 +236,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
|||||||
private const val DATA_PROGRESS = "progress"
|
private const val DATA_PROGRESS = "progress"
|
||||||
private const val DATA_TOTAL = "total"
|
private const val DATA_TOTAL = "total"
|
||||||
private const val TAG = "tracking"
|
private const val TAG = "tracking"
|
||||||
|
private const val TAG_ONESHOT = "tracking_oneshot"
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
private fun createNotificationChannel(context: Context) {
|
private fun createNotificationChannel(context: Context) {
|
||||||
@@ -277,7 +277,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
|||||||
.build()
|
.build()
|
||||||
val request = OneTimeWorkRequestBuilder<TrackWorker>()
|
val request = OneTimeWorkRequestBuilder<TrackWorker>()
|
||||||
.setConstraints(constraints)
|
.setConstraints(constraints)
|
||||||
.addTag(TAG)
|
.addTag(TAG_ONESHOT)
|
||||||
.build()
|
.build()
|
||||||
WorkManager.getInstance(context)
|
WorkManager.getInstance(context)
|
||||||
.enqueue(request)
|
.enqueue(request)
|
||||||
|
|||||||
@@ -249,4 +249,12 @@
|
|||||||
<string name="available_sources">Доступные источники</string>
|
<string name="available_sources">Доступные источники</string>
|
||||||
<string name="dynamic_theme">Динамическая тема</string>
|
<string name="dynamic_theme">Динамическая тема</string>
|
||||||
<string name="dynamic_theme_summary">Применяет тему приложения, основанную на цветовой палитре обоев на устройстве</string>
|
<string name="dynamic_theme_summary">Применяет тему приложения, основанную на цветовой палитре обоев на устройстве</string>
|
||||||
|
<string name="suggestions">Рекомендации</string>
|
||||||
|
<string name="suggestions_enable">Включить рекомендации</string>
|
||||||
|
<string name="suggestions_summary">Предлагать мангу на основе Ваших предпочтений</string>
|
||||||
|
<string name="suggestions_info">Все данные анализируются локально на устройстве. Ваши персональные данные не передаются в какие-либо сервисы</string>
|
||||||
|
<string name="text_suggestion_holder">Начните читать мангу, чтобы получать персональные предложения</string>
|
||||||
|
<string name="exclude_nsfw_from_suggestions">Не предлагать NSFW мангу</string>
|
||||||
|
<string name="enabled">Включено</string>
|
||||||
|
<string name="disabled">Выключено</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -251,6 +251,12 @@
|
|||||||
<string name="dynamic_theme">Dynamic theme</string>
|
<string name="dynamic_theme">Dynamic theme</string>
|
||||||
<string name="dynamic_theme_summary">Applies a theme created on the color scheme of your wallpaper</string>
|
<string name="dynamic_theme_summary">Applies a theme created on the color scheme of your wallpaper</string>
|
||||||
<string name="importing_progress">Importing manga: %1$d of %2$d</string>
|
<string name="importing_progress">Importing manga: %1$d of %2$d</string>
|
||||||
<string name="suggestions">Suggestions</string>
|
<string name="suggestions">Suggestions</string>
|
||||||
<string name="text_suggestion_holder">Start reading manga and you will get personalized suggestions</string>
|
<string name="suggestions_enable">Enable suggestions</string>
|
||||||
|
<string name="suggestions_summary">Suggest manga based on your preferences</string>
|
||||||
|
<string name="suggestions_info">All data is analyzed locally on this device. There is no transfer of your personal data to any services</string>
|
||||||
|
<string name="text_suggestion_holder">Start reading manga and you will get personalized suggestions</string>
|
||||||
|
<string name="exclude_nsfw_from_suggestions">Do not suggest NSFW manga</string>
|
||||||
|
<string name="enabled">Enabled</string>
|
||||||
|
<string name="disabled">Disabled</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -61,6 +61,13 @@
|
|||||||
app:allowDividerAbove="true"
|
app:allowDividerAbove="true"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
android:fragment="org.koitharu.kotatsu.settings.SuggestionsSettingsFragment"
|
||||||
|
android:key="suggestions"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/suggestions"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="local_storage"
|
android:key="local_storage"
|
||||||
android:title="@string/manga_save_location"
|
android:title="@string/manga_save_location"
|
||||||
@@ -71,7 +78,7 @@
|
|||||||
android:title="@string/history_and_cache"
|
android:title="@string/history_and_cache"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreferenceCompat
|
||||||
android:key="protect_app"
|
android:key="protect_app"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:summary="@string/protect_application_summary"
|
android:summary="@string/protect_application_summary"
|
||||||
|
|||||||
27
app/src/main/res/xml/pref_suggestions.xml
Normal file
27
app/src/main/res/xml/pref_suggestions.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="suggestions"
|
||||||
|
android:summary="@string/suggestions_summary"
|
||||||
|
android:title="@string/suggestions_enable"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:dependency="suggestions"
|
||||||
|
android:key="suggestions_exclude_nsfw"
|
||||||
|
android:title="@string/exclude_nsfw_from_suggestions"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_info_outline"
|
||||||
|
android:key="track_warning"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="false"
|
||||||
|
android:summary="@string/suggestions_info"
|
||||||
|
app:allowDividerAbove="true" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user