Merge branch 'devel' into feature/suggestions
This commit is contained in:
@@ -83,7 +83,7 @@ dependencies {
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
implementation 'com.google.android.material:material:1.6.0-alpha02'
|
||||
implementation 'com.google.android.material:material:1.6.0-alpha03'
|
||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.4.1'
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@ package org.koitharu.kotatsu.base.ui.widgets
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import org.koitharu.kotatsu.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class CoverImageView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0,
|
||||
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
||||
) : ShapeableImageView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var orientation: Int = HORIZONTAL
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ abstract class TracksDao {
|
||||
@Query("SELECT * FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract suspend fun find(mangaId: Long): TrackEntity?
|
||||
|
||||
@Query("SELECT chapters_new FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract suspend fun findNewChapters(mangaId: Long): Int?
|
||||
|
||||
@Query("DELETE FROM tracks")
|
||||
abstract suspend fun clear()
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ class AppSettings(context: Context) {
|
||||
get() = prefs.getBoolean(KEY_TRAFFIC_WARNING, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_TRAFFIC_WARNING, value) }
|
||||
|
||||
val appUpdateAuto: Boolean
|
||||
val isUpdateCheckingEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_APP_UPDATE_AUTO, true)
|
||||
|
||||
var appUpdate: Long
|
||||
var lastUpdateCheckTimestamp: Long
|
||||
get() = prefs.getLong(KEY_APP_UPDATE, 0L)
|
||||
set(value) = prefs.edit { putLong(KEY_APP_UPDATE, value) }
|
||||
|
||||
@@ -123,6 +123,12 @@ class AppSettings(context: Context) {
|
||||
val isPagesNumbersEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_PAGES_NUMBERS, false)
|
||||
|
||||
val screenshotsPolicy: ScreenshotsPolicy
|
||||
get() = runCatching {
|
||||
val key = prefs.getString(KEY_SCREENSHOTS_POLICY, null)?.uppercase(Locale.ROOT)
|
||||
if (key == null) ScreenshotsPolicy.ALLOW else ScreenshotsPolicy.valueOf(key)
|
||||
}.getOrDefault(ScreenshotsPolicy.ALLOW)
|
||||
|
||||
var mangaStorageDir: File?
|
||||
get() = prefs.getString(KEY_LOCAL_STORAGE, null)?.let {
|
||||
File(it)
|
||||
@@ -230,6 +236,7 @@ class AppSettings(context: Context) {
|
||||
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
||||
const val KEY_HISTORY_EXCLUDE_NSFW = "history_exclude_nsfw"
|
||||
const val KEY_PAGES_NUMBERS = "pages_numbers"
|
||||
const val KEY_SCREENSHOTS_POLICY = "screenshots_policy"
|
||||
const val KEY_SUGGESTIONS = "suggestions"
|
||||
const val KEY_SUGGESTIONS_EXCLUDE_NSFW = "suggestions_exclude_nsfw"
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
enum class ScreenshotsPolicy {
|
||||
|
||||
// Do not rename this
|
||||
ALLOW, BLOCK_NSFW, BLOCK_ALL;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ val favouritesModule
|
||||
single { FavouritesRepository(get()) }
|
||||
|
||||
viewModel { categoryId ->
|
||||
FavouritesListViewModel(categoryId.get(), get(), get())
|
||||
FavouritesListViewModel(categoryId.get(), get(), get(), get())
|
||||
}
|
||||
viewModel { FavouritesCategoriesViewModel(get()) }
|
||||
viewModel { manga ->
|
||||
|
||||
@@ -148,7 +148,7 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
target: RecyclerView.ViewHolder,
|
||||
): Boolean = true
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
||||
@@ -160,7 +160,7 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
|
||||
target: RecyclerView.ViewHolder,
|
||||
toPos: Int,
|
||||
x: Int,
|
||||
y: Int
|
||||
y: Int,
|
||||
) {
|
||||
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
|
||||
viewModel.reorderCategories(fromPos, toPos)
|
||||
|
||||
@@ -9,18 +9,21 @@ import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.list.domain.CountersProvider
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
|
||||
class FavouritesListViewModel(
|
||||
private val categoryId: Long,
|
||||
private val repository: FavouritesRepository,
|
||||
settings: AppSettings
|
||||
) : MangaListViewModel(settings) {
|
||||
private val trackingRepository: TrackingRepository,
|
||||
settings: AppSettings,
|
||||
) : MangaListViewModel(settings), CountersProvider {
|
||||
|
||||
override val content = combine(
|
||||
if (categoryId == 0L) {
|
||||
@@ -42,7 +45,7 @@ class FavouritesListViewModel(
|
||||
}
|
||||
)
|
||||
)
|
||||
else -> list.toUi(mode)
|
||||
else -> list.toUi(mode, this)
|
||||
}
|
||||
}.catch {
|
||||
emit(listOf(it.toErrorState(canRetry = false)))
|
||||
@@ -61,4 +64,8 @@ class FavouritesListViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCounter(mangaId: Long): Int {
|
||||
return trackingRepository.getNewChaptersCount(mangaId)
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,5 @@ val historyModule
|
||||
get() = module {
|
||||
|
||||
single { HistoryRepository(get(), get(), get()) }
|
||||
viewModel { HistoryListViewModel(get(), get(), get()) }
|
||||
viewModel { HistoryListViewModel(get(), get(), get(), get()) }
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.*
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.daysDiff
|
||||
@@ -24,7 +25,8 @@ import java.util.concurrent.TimeUnit
|
||||
class HistoryListViewModel(
|
||||
private val repository: HistoryRepository,
|
||||
private val settings: AppSettings,
|
||||
private val shortcutsRepository: ShortcutsRepository
|
||||
private val shortcutsRepository: ShortcutsRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
) : MangaListViewModel(settings) {
|
||||
|
||||
val onItemRemoved = SingleLiveEvent<Manga>()
|
||||
@@ -75,7 +77,7 @@ class HistoryListViewModel(
|
||||
settings.historyGrouping = isGroupingEnabled
|
||||
}
|
||||
|
||||
private fun mapList(
|
||||
private suspend fun mapList(
|
||||
list: List<MangaWithHistory>,
|
||||
grouped: Boolean,
|
||||
mode: ListMode
|
||||
@@ -93,10 +95,11 @@ class HistoryListViewModel(
|
||||
}
|
||||
prevDate = date
|
||||
}
|
||||
val counter = trackingRepository.getNewChaptersCount(manga.id)
|
||||
result += when (mode) {
|
||||
ListMode.LIST -> manga.toListModel()
|
||||
ListMode.DETAILED_LIST -> manga.toListDetailedModel()
|
||||
ListMode.GRID -> manga.toGridModel()
|
||||
ListMode.LIST -> manga.toListModel(counter)
|
||||
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter)
|
||||
ListMode.GRID -> manga.toGridModel(counter)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.koitharu.kotatsu.list.domain
|
||||
|
||||
fun interface CountersProvider {
|
||||
|
||||
suspend fun getCounter(mangaId: Long): Int
|
||||
}
|
||||
@@ -257,7 +257,7 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
||||
}
|
||||
ListMode.DETAILED_LIST -> {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
val spacing = resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
||||
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
||||
updatePadding(left = spacing, right = spacing)
|
||||
addItemDecoration(SpacingItemDecoration(spacing))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
@file:SuppressLint("UnsafeOptInUsageError")
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.badge.BadgeUtils
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
fun View.bindBadge(badge: BadgeDrawable?, counter: Int): BadgeDrawable? {
|
||||
return if (counter > 0) {
|
||||
val badgeDrawable = badge ?: initBadge(this)
|
||||
badgeDrawable.number = counter
|
||||
badgeDrawable.isVisible = true
|
||||
badgeDrawable.align()
|
||||
badgeDrawable
|
||||
} else {
|
||||
badge?.isVisible = false
|
||||
badge
|
||||
}
|
||||
}
|
||||
|
||||
fun View.clearBadge(badge: BadgeDrawable?) {
|
||||
BadgeUtils.detachBadgeDrawable(badge, this)
|
||||
}
|
||||
|
||||
private fun initBadge(anchor: View): BadgeDrawable {
|
||||
val badge = BadgeDrawable.create(anchor.context)
|
||||
val resources = anchor.resources
|
||||
badge.maxCharacterCount = resources.getInteger(R.integer.manga_badge_max_character_count)
|
||||
badge.horizontalOffsetWithoutText = resources.getDimensionPixelOffset(R.dimen.manga_badge_offset_horizontal)
|
||||
badge.verticalOffsetWithoutText = resources.getDimensionPixelOffset(R.dimen.manga_badge_offset_vertical)
|
||||
anchor.doOnNextLayout {
|
||||
BadgeUtils.attachBadgeDrawable(badge, it)
|
||||
badge.align()
|
||||
}
|
||||
return badge
|
||||
}
|
||||
|
||||
private fun BadgeDrawable.align() {
|
||||
horizontalOffsetWithText = horizontalOffsetWithoutText + intrinsicWidth / 2
|
||||
verticalOffsetWithText = verticalOffsetWithoutText + intrinsicHeight / 2
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.request.Disposable
|
||||
import coil.util.CoilUtils
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
@@ -24,6 +25,7 @@ fun mangaGridItemAD(
|
||||
) {
|
||||
|
||||
var imageRequest: Disposable? = null
|
||||
var badge: BadgeDrawable? = null
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickListener.onItemClick(item.manga, it)
|
||||
@@ -43,9 +45,12 @@ fun mangaGridItemAD(
|
||||
.allowRgb565(true)
|
||||
.lifecycle(lifecycleOwner)
|
||||
.enqueueWith(coil)
|
||||
badge = itemView.bindBadge(badge, item.counter)
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
itemView.clearBadge(badge)
|
||||
badge = null
|
||||
imageRequest?.dispose()
|
||||
CoilUtils.clear(binding.imageViewCover)
|
||||
binding.imageViewCover.setImageDrawable(null)
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.request.Disposable
|
||||
import coil.util.CoilUtils
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
@@ -25,6 +26,7 @@ fun mangaListDetailedItemAD(
|
||||
) {
|
||||
|
||||
var imageRequest: Disposable? = null
|
||||
var badge: BadgeDrawable? = null
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickListener.onItemClick(item.manga, it)
|
||||
@@ -47,9 +49,12 @@ fun mangaListDetailedItemAD(
|
||||
.enqueueWith(coil)
|
||||
binding.textViewRating.textAndVisible = item.rating
|
||||
binding.textViewTags.text = item.tags
|
||||
itemView.bindBadge(badge, item.counter)
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
itemView.clearBadge(badge)
|
||||
badge = null
|
||||
imageRequest?.dispose()
|
||||
CoilUtils.clear(binding.imageViewCover)
|
||||
binding.imageViewCover.setImageDrawable(null)
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.request.Disposable
|
||||
import coil.util.CoilUtils
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
@@ -25,6 +26,7 @@ fun mangaListItemAD(
|
||||
) {
|
||||
|
||||
var imageRequest: Disposable? = null
|
||||
var badge: BadgeDrawable? = null
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickListener.onItemClick(item.manga, it)
|
||||
@@ -45,9 +47,12 @@ fun mangaListItemAD(
|
||||
.allowRgb565(true)
|
||||
.lifecycle(lifecycleOwner)
|
||||
.enqueueWith(coil)
|
||||
itemView.bindBadge(badge, item.counter)
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
itemView.clearBadge(badge)
|
||||
badge = null
|
||||
imageRequest?.dispose()
|
||||
CoilUtils.clear(binding.imageViewCover)
|
||||
binding.imageViewCover.setImageDrawable(null)
|
||||
|
||||
@@ -6,44 +6,71 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.list.domain.CountersProvider
|
||||
|
||||
fun Manga.toListModel() = MangaListModel(
|
||||
fun Manga.toListModel(counter: Int) = MangaListModel(
|
||||
id = id,
|
||||
title = title,
|
||||
subtitle = tags.joinToString(", ") { it.title },
|
||||
coverUrl = coverUrl,
|
||||
manga = this
|
||||
manga = this,
|
||||
counter = counter,
|
||||
)
|
||||
|
||||
fun Manga.toListDetailedModel() = MangaListDetailedModel(
|
||||
fun Manga.toListDetailedModel(counter: Int) = MangaListDetailedModel(
|
||||
id = id,
|
||||
title = title,
|
||||
subtitle = altTitle,
|
||||
rating = if (rating == Manga.NO_RATING) null else String.format("%.1f", rating * 5),
|
||||
tags = tags.joinToString(", ") { it.title },
|
||||
coverUrl = coverUrl,
|
||||
manga = this
|
||||
manga = this,
|
||||
counter = counter,
|
||||
)
|
||||
|
||||
fun Manga.toGridModel() = MangaGridModel(
|
||||
fun Manga.toGridModel(counter: Int) = MangaGridModel(
|
||||
id = id,
|
||||
title = title,
|
||||
coverUrl = coverUrl,
|
||||
manga = this
|
||||
manga = this,
|
||||
counter = counter,
|
||||
)
|
||||
|
||||
fun List<Manga>.toUi(mode: ListMode): List<ListModel> = when (mode) {
|
||||
ListMode.LIST -> map(Manga::toListModel)
|
||||
ListMode.DETAILED_LIST -> map(Manga::toListDetailedModel)
|
||||
ListMode.GRID -> map(Manga::toGridModel)
|
||||
suspend fun List<Manga>.toUi(
|
||||
mode: ListMode,
|
||||
countersProvider: CountersProvider,
|
||||
): List<ListModel> = when (mode) {
|
||||
ListMode.LIST -> map { it.toListModel(countersProvider.getCounter(it.id)) }
|
||||
ListMode.DETAILED_LIST -> map { it.toListDetailedModel(countersProvider.getCounter(it.id)) }
|
||||
ListMode.GRID -> map { it.toGridModel(countersProvider.getCounter(it.id)) }
|
||||
}
|
||||
|
||||
fun <C : MutableCollection<ListModel>> List<Manga>.toUi(destination: C, mode: ListMode): C =
|
||||
when (mode) {
|
||||
ListMode.LIST -> mapTo(destination, Manga::toListModel)
|
||||
ListMode.DETAILED_LIST -> mapTo(destination, Manga::toListDetailedModel)
|
||||
ListMode.GRID -> mapTo(destination, Manga::toGridModel)
|
||||
}
|
||||
suspend fun <C : MutableCollection<ListModel>> List<Manga>.toUi(
|
||||
destination: C,
|
||||
mode: ListMode,
|
||||
countersProvider: CountersProvider,
|
||||
): C = when (mode) {
|
||||
ListMode.LIST -> mapTo(destination) { it.toListModel(countersProvider.getCounter(it.id)) }
|
||||
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(countersProvider.getCounter(it.id)) }
|
||||
ListMode.GRID -> mapTo(destination) { it.toGridModel(countersProvider.getCounter(it.id)) }
|
||||
}
|
||||
|
||||
fun List<Manga>.toUi(
|
||||
mode: ListMode,
|
||||
): List<ListModel> = when (mode) {
|
||||
ListMode.LIST -> map { it.toListModel(0) }
|
||||
ListMode.DETAILED_LIST -> map { it.toListDetailedModel(0) }
|
||||
ListMode.GRID -> map { it.toGridModel(0) }
|
||||
}
|
||||
|
||||
fun <C : MutableCollection<ListModel>> List<Manga>.toUi(
|
||||
destination: C,
|
||||
mode: ListMode,
|
||||
): C = when (mode) {
|
||||
ListMode.LIST -> mapTo(destination) { it.toListModel(0) }
|
||||
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(0) }
|
||||
ListMode.GRID -> mapTo(destination) { it.toGridModel(0) }
|
||||
}
|
||||
|
||||
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(
|
||||
exception = this,
|
||||
|
||||
@@ -6,5 +6,6 @@ data class MangaGridModel(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val coverUrl: String,
|
||||
val manga: Manga
|
||||
val manga: Manga,
|
||||
val counter: Int,
|
||||
) : ListModel
|
||||
@@ -9,5 +9,6 @@ data class MangaListDetailedModel(
|
||||
val tags: String,
|
||||
val coverUrl: String,
|
||||
val rating: String?,
|
||||
val manga: Manga
|
||||
val manga: Manga,
|
||||
val counter: Int,
|
||||
) : ListModel
|
||||
@@ -7,5 +7,6 @@ data class MangaListModel(
|
||||
val title: String,
|
||||
val subtitle: String,
|
||||
val coverUrl: String,
|
||||
val manga: Manga
|
||||
val manga: Manga,
|
||||
val counter: Int,
|
||||
) : ListModel
|
||||
@@ -43,7 +43,13 @@ class LocalListViewModel(
|
||||
when {
|
||||
error != null -> listOf(error.toErrorState(canRetry = true))
|
||||
list == null -> listOf(LoadingState)
|
||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_storage, R.string.text_local_holder_primary, R.string.text_local_holder_secondary))
|
||||
list.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
R.drawable.ic_storage,
|
||||
R.string.text_local_holder_primary,
|
||||
R.string.text_local_holder_secondary
|
||||
)
|
||||
)
|
||||
else -> ArrayList<ListModel>(list.size + 1).apply {
|
||||
add(headerModel)
|
||||
list.toUi(this, mode)
|
||||
|
||||
@@ -15,12 +15,16 @@ import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -28,6 +32,7 @@ import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.prefs.AppSection
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ActivityMainBinding
|
||||
import org.koitharu.kotatsu.databinding.NavigationHeaderBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
@@ -356,10 +361,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
}
|
||||
|
||||
private fun onFirstStart() {
|
||||
TrackWorker.setup(applicationContext)
|
||||
SuggestionsWorker.setup(applicationContext)
|
||||
AppUpdateChecker(this@MainActivity).launchIfNeeded()
|
||||
OnboardDialogFragment.showWelcome(get(), supportFragmentManager)
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
TrackWorker.setup(applicationContext)
|
||||
SuggestionsWorker.setup(applicationContext)
|
||||
AppUpdateChecker(this@MainActivity).checkIfNeeded()
|
||||
if (!get<AppSettings>().isSourcesSelected) {
|
||||
withContext(Dispatchers.Main) {
|
||||
OnboardDialogFragment.showWelcome(supportFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.Insets
|
||||
@@ -23,6 +24,7 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
setContentView(ActivityProtectBinding.inflate(layoutInflater))
|
||||
binding.editPassword.setOnEditorActionListener(this)
|
||||
binding.editPassword.addTextChangedListener(this)
|
||||
|
||||
@@ -94,6 +94,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
viewModel.content.observe(this) {
|
||||
onLoadingStateChanged(viewModel.isLoading.value == true)
|
||||
}
|
||||
viewModel.isScreenshotsBlockEnabled.observe(this, this::setWindowSecure)
|
||||
}
|
||||
|
||||
private fun onInitReader(mode: ReaderMode) {
|
||||
@@ -299,6 +300,14 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
}.show()
|
||||
}
|
||||
|
||||
private fun setWindowSecure(isSecure: Boolean) {
|
||||
if (isSecure) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUiIsVisible(isUiVisible: Boolean) {
|
||||
if (binding.appbarTop.isVisible != isUiVisible) {
|
||||
if (isUiVisible) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.koitharu.kotatsu.core.os.ShortcutsRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.prefs.ScreenshotsPolicy
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||
@@ -69,6 +70,17 @@ class ReaderViewModel(
|
||||
.onStart { emit(settings.readerAnimation) }
|
||||
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.IO)
|
||||
|
||||
val isScreenshotsBlockEnabled = combine(
|
||||
mangaData,
|
||||
settings.observe()
|
||||
.filter { it == AppSettings.KEY_SCREENSHOTS_POLICY }
|
||||
.onStart { emit("") }
|
||||
.map { settings.screenshotsPolicy },
|
||||
) { manga, policy ->
|
||||
policy == ScreenshotsPolicy.BLOCK_ALL ||
|
||||
(policy == ScreenshotsPolicy.BLOCK_NSFW && manga != null && manga.isNsfw)
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.IO)
|
||||
|
||||
val onZoomChanged = SingleLiveEvent<Unit>()
|
||||
|
||||
init {
|
||||
|
||||
@@ -7,11 +7,8 @@ import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
@@ -37,44 +34,31 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
|
||||
private val settings = activity.get<AppSettings>()
|
||||
private val repo = activity.get<GithubRepository>()
|
||||
|
||||
fun launchIfNeeded(): Job? {
|
||||
return if (settings.appUpdateAuto && settings.appUpdate + PERIOD < System.currentTimeMillis()) {
|
||||
launch()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun launch(): Job? {
|
||||
return if (isUpdateSupported(activity)) {
|
||||
launchInternal()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
suspend fun checkIfNeeded(): Boolean? = if (
|
||||
settings.isUpdateCheckingEnabled &&
|
||||
settings.lastUpdateCheckTimestamp + PERIOD < System.currentTimeMillis()
|
||||
) {
|
||||
checkNow()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
suspend fun checkNow() = runCatching {
|
||||
withContext(Dispatchers.Default) {
|
||||
val version = repo.getLatestVersion()
|
||||
val newVersionId = VersionId.parse(version.name)
|
||||
val currentVersionId = VersionId.parse(BuildConfig.VERSION_NAME)
|
||||
val result = newVersionId > currentVersionId
|
||||
if (result) {
|
||||
withContext(Dispatchers.Main) {
|
||||
showUpdateDialog(version)
|
||||
}
|
||||
val version = repo.getLatestVersion()
|
||||
val newVersionId = VersionId.parse(version.name)
|
||||
val currentVersionId = VersionId.parse(BuildConfig.VERSION_NAME)
|
||||
val result = newVersionId > currentVersionId
|
||||
if (result) {
|
||||
withContext(Dispatchers.Main) {
|
||||
showUpdateDialog(version)
|
||||
}
|
||||
settings.appUpdate = System.currentTimeMillis()
|
||||
result
|
||||
}
|
||||
settings.lastUpdateCheckTimestamp = System.currentTimeMillis()
|
||||
result
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.getOrNull()
|
||||
|
||||
private fun launchInternal() = activity.lifecycleScope.launch {
|
||||
checkNow()
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun showUpdateDialog(version: AppVersion) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
|
||||
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocalesAdapter
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
@@ -75,12 +74,10 @@ class OnboardDialogFragment : AlertDialogFragment<DialogOnboardBinding>(),
|
||||
|
||||
fun show(fm: FragmentManager) = OnboardDialogFragment().show(fm, TAG)
|
||||
|
||||
fun showWelcome(settings: AppSettings, fm: FragmentManager) {
|
||||
if (!settings.isSourcesSelected) {
|
||||
OnboardDialogFragment().withArgs(1) {
|
||||
putBoolean(ARG_WELCOME, true)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
fun showWelcome(fm: FragmentManager) {
|
||||
OnboardDialogFragment().withArgs(1) {
|
||||
putBoolean(ARG_WELCOME, true)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.Insets
|
||||
@@ -21,6 +22,7 @@ class ProtectSetupActivity : BaseActivity<ActivitySetupProtectBinding>(), TextWa
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
setContentView(ActivitySetupProtectBinding.inflate(layoutInflater))
|
||||
binding.editPassword.addTextChangedListener(this)
|
||||
binding.editPassword.setOnEditorActionListener(this)
|
||||
|
||||
@@ -5,11 +5,12 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.*
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.onFirst
|
||||
@@ -29,7 +30,7 @@ class SuggestionsViewModel(
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = R.string.text_suggestion_holder,
|
||||
))
|
||||
else -> mapList(list, mode)
|
||||
else -> list.toUi(mode)
|
||||
}
|
||||
}.onFirst {
|
||||
isLoading.postValue(false)
|
||||
@@ -43,15 +44,4 @@ class SuggestionsViewModel(
|
||||
override fun onRefresh() = Unit
|
||||
|
||||
override fun onRetry() = Unit
|
||||
|
||||
private fun mapList(
|
||||
list: List<Manga>,
|
||||
mode: ListMode,
|
||||
): List<ListModel> = list.map { manga ->
|
||||
when (mode) {
|
||||
ListMode.LIST -> manga.toListModel()
|
||||
ListMode.DETAILED_LIST -> manga.toListDetailedModel()
|
||||
ListMode.GRID -> manga.toGridModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@ class TrackingRepository(
|
||||
) {
|
||||
|
||||
suspend fun getNewChaptersCount(mangaId: Long): Int {
|
||||
val entity = db.tracksDao.find(mangaId) ?: return 0
|
||||
return entity.newChapters
|
||||
return db.tracksDao.findNewChapters(mangaId) ?: 0
|
||||
}
|
||||
|
||||
suspend fun getAllTracks(useFavourites: Boolean, useHistory: Boolean): List<MangaTracking> {
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
fun navigationItemBackground(context: Context): Drawable? {
|
||||
// Need to inflate the drawable and CSL via AppCompatResources to work on Lollipop
|
||||
// From Google I/O repo (https://github.com/google/iosched)
|
||||
var background = AppCompatResources.getDrawable(context, R.drawable.navigation_item_background)
|
||||
if (background != null) {
|
||||
val tint = AppCompatResources.getColorStateList(
|
||||
context, R.color.navigation_item_background_tint
|
||||
)
|
||||
background = DrawableCompat.wrap(background.mutate())
|
||||
background.setTintList(tint)
|
||||
}
|
||||
return background
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.retry
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
@@ -21,14 +23,14 @@ class WidgetUpdater(private val context: Context) {
|
||||
repository.observeAll(SortOrder.NEWEST)
|
||||
.onEach { updateWidget(ShelfWidgetProvider::class.java) }
|
||||
.retry { error -> error !is CancellationException }
|
||||
.launchIn(processLifecycleScope)
|
||||
.launchIn(processLifecycleScope + Dispatchers.Default)
|
||||
}
|
||||
|
||||
fun subscribeToHistory(repository: HistoryRepository) {
|
||||
repository.observeAll()
|
||||
.onEach { updateWidget(RecentWidgetProvider::class.java) }
|
||||
.retry { error -> error !is CancellationException }
|
||||
.launchIn(processLifecycleScope)
|
||||
.launchIn(processLifecycleScope + Dispatchers.Default)
|
||||
}
|
||||
|
||||
private fun updateWidget(cls: Class<*>) {
|
||||
|
||||
4
app/src/main/res/color/navigation_bar_scrim.xml
Normal file
4
app/src/main/res/color/navigation_bar_scrim.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.7" android:color="?attr/colorSurface" />
|
||||
</selector>
|
||||
@@ -10,7 +10,7 @@
|
||||
android:bottom="2dp"
|
||||
android:left="2dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<corners android:radius="12dp" />
|
||||
<solid android:color="@color/selector_overlay" />
|
||||
</shape>
|
||||
</item>
|
||||
@@ -22,7 +22,7 @@
|
||||
android:bottom="2dp"
|
||||
android:left="2dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<corners android:radius="12dp" />
|
||||
<solid android:color="@color/selector_overlay" />
|
||||
</shape>
|
||||
</item>
|
||||
@@ -33,7 +33,7 @@
|
||||
android:bottom="2dp"
|
||||
android:left="2dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<corners android:radius="12dp" />
|
||||
<solid android:color="?android:attr/windowBackground" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true">
|
||||
<inset
|
||||
android:insetLeft="@dimen/nav_item_background_inset_left"
|
||||
android:insetRight="@dimen/nav_item_background_inset_right">
|
||||
<shape>
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/nav_item_background_corner_radius_left"
|
||||
android:bottomRightRadius="@dimen/nav_item_background_corner_radius_right"
|
||||
android:topLeftRadius="@dimen/nav_item_background_corner_radius_left"
|
||||
android:topRightRadius="@dimen/nav_item_background_corner_radius_right" />
|
||||
</shape>
|
||||
</inset>
|
||||
</item>
|
||||
<item>
|
||||
<color android:color="@android:color/transparent" />
|
||||
</item>
|
||||
</selector>
|
||||
@@ -15,7 +15,7 @@
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"/>
|
||||
android:theme="?attr/actionBarTheme" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
@@ -27,21 +27,21 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/toolbar_card"
|
||||
android:background="@drawable/toolbar_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/toolbar_background">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:background="@android:color/transparent"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:titleTextAppearance="@style/TextAppearance.Kotatsu.PersistentToolbarTitle"
|
||||
app:titleTextColor="?android:colorControlNormal"
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||
android:id="@+id/chips_tags"
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|start"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
||||
tools:text="@tools:sample/lorem[2]" />
|
||||
@@ -4,16 +4,16 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@drawable/list_selector"
|
||||
android:clipChildren="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp">
|
||||
app:cardCornerRadius="12dp">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
|
||||
android:id="@+id/imageView_cover"
|
||||
@@ -28,15 +28,15 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
style="?attr/textAppearanceBodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elegantTextHeight="false"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingExtra="-2dp"
|
||||
android:maxLines="2"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:padding="4dp"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/thumbnail"
|
||||
tools:text="Sample name" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,17 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/manga_list_item_height"
|
||||
android:background="@drawable/list_selector"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
app:cardCornerRadius="12dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="8dp">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
|
||||
android:id="@+id/imageView_cover"
|
||||
@@ -19,37 +18,38 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:scaleType="centerCrop"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/manga_list_details_item_height"
|
||||
android:background="@drawable/list_selector"
|
||||
android:orientation="horizontal">
|
||||
app:cardCornerRadius="12dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="8dp">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
|
||||
android:id="@+id/imageView_cover"
|
||||
@@ -19,70 +18,71 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/backgrounds/scenic"/>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="none"
|
||||
android:gravity="center_vertical"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
tools:src="@tools:sample/backgrounds/scenic"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_tags"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="none"
|
||||
android:gravity="center_vertical"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_rating"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:drawablePadding="4dp"
|
||||
android:paddingStart="6dp"
|
||||
app:drawableEndCompat="@drawable/ic_star"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="9.6" />
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_tags"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="none"
|
||||
android:gravity="center_vertical"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_rating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:drawablePadding="4dp"
|
||||
android:paddingStart="6dp"
|
||||
app:drawableEndCompat="@drawable/ic_star"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="9.6" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:background="?android:windowBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
@@ -249,6 +249,10 @@
|
||||
<string name="available_sources">Доступные источники</string>
|
||||
<string name="dynamic_theme">Динамическая тема</string>
|
||||
<string name="dynamic_theme_summary">Применяет тему приложения, основанную на цветовой палитре обоев на устройстве</string>
|
||||
<string name="screenshots_policy">Разрешить скриншоты</string>
|
||||
<string name="screenshots_allow">Разрешить</string>
|
||||
<string name="screenshots_block_nsfw">Запретить для NSFW</string>
|
||||
<string name="screenshots_block_all">Запретить всегда</string>
|
||||
<string name="suggestions">Рекомендации</string>
|
||||
<string name="suggestions_enable">Включить рекомендации</string>
|
||||
<string name="suggestions_summary">Предлагать мангу на основе Ваших предпочтений</string>
|
||||
|
||||
@@ -57,4 +57,69 @@
|
||||
<string name="details">Detaylar</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="page_saved">Kaydet</string>
|
||||
<string name="restart">Tekrar başlat</string>
|
||||
<string name="error_occurred">Bir hata oluştu</string>
|
||||
<string name="remote_sources">Uzak kaynaklar</string>
|
||||
<string name="warning">Uyarı</string>
|
||||
<string name="history_and_cache">Geçmiş ve önbellek</string>
|
||||
<string name="search_history_cleared">Temizlendi</string>
|
||||
<string name="_continue">Devam</string>
|
||||
<string name="not_available">Müsait değil</string>
|
||||
<string name="favourites_category_empty">Boş kategori</string>
|
||||
<string name="cache">Önbellek</string>
|
||||
<string name="application_update">Uygulamanın yeni sürümlerini kontrol edin</string>
|
||||
<string name="remove_category">Kaldır</string>
|
||||
<string name="delete">Sil</string>
|
||||
<string name="chapter_d_of_d">Bölüm %1$d / %2$d</string>
|
||||
<string name="text_file_not_supported">Bir ZIP veya CBZ dosyası seçin.</string>
|
||||
<string name="read_mode">Okuma modu</string>
|
||||
<string name="grid_size">Izgara boyutu</string>
|
||||
<string name="webtoon">Webtoon</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="reader_settings">Okuyucu ayarları</string>
|
||||
<string name="volume_buttons">Ses butonları</string>
|
||||
<string name="dont_ask_again">Bir daha sorma</string>
|
||||
<string name="cancelling_">İptal ediliyor…</string>
|
||||
<string name="error">Hata</string>
|
||||
<string name="clear_thumbs_cache">Küçük resim önbelleğini temizle</string>
|
||||
<string name="gestures_only">Yalnızca hareketler</string>
|
||||
<string name="domain">Alan adi</string>
|
||||
<string name="open_in_browser">Web tarayıcısında aç</string>
|
||||
<string name="new_chapters">Yeni bölümler</string>
|
||||
<string name="notifications_settings">Bildirim ayarları</string>
|
||||
<string name="notification_sound">Bildirim sesi</string>
|
||||
<string name="light_indicator">LED göstergesi</string>
|
||||
<string name="vibration">Titreşim</string>
|
||||
<string name="other_storage">Diğer depolama</string>
|
||||
<string name="updates">Güncellemeler</string>
|
||||
<string name="create_shortcut">Kısayol oluştur…</string>
|
||||
<string name="_import">İçe aktar</string>
|
||||
<string name="delete_manga">Mangayı sil</string>
|
||||
<string name="computing_">Bilgi işleniyor…</string>
|
||||
<string name="sort_order">Sıralama düzeni</string>
|
||||
<string name="no_description">Açıklama yok</string>
|
||||
<string name="operation_not_supported">Bu işlem desteklenmiyor</string>
|
||||
<string name="standard">Standart</string>
|
||||
<string name="clear_pages_cache">Sayfa önbelleğini temizle</string>
|
||||
<string name="search_on_s">%s üzerinde ara</string>
|
||||
<string name="internal_storage">Dahili depolama</string>
|
||||
<string name="notifications">Bildirimler</string>
|
||||
<string name="switch_pages">Sayfaları değiştir</string>
|
||||
<string name="network_consumption_warning">Bu çok fazla veri aktarabilir</string>
|
||||
<string name="save_manga">Kaydet</string>
|
||||
<string name="download">İndir</string>
|
||||
<string name="manga_save_location">İndirilenler klasörü</string>
|
||||
<string name="external_storage">Harici depolama</string>
|
||||
<string name="read_from_start">Baştan oku</string>
|
||||
<string name="categories_">Kategoriler…</string>
|
||||
<string name="app_update_available">Uygulamanın yeni bir sürümü mevcut</string>
|
||||
<string name="show_notification_app_update">Yeni bir sürüm mevcutsa bildirimini göster</string>
|
||||
<string name="favourites_categories">Favori kategoriler</string>
|
||||
<string name="done">Bitti</string>
|
||||
<string name="read_later">Sonra oku</string>
|
||||
<string name="pages_animation">Sayfa animasyonu</string>
|
||||
<string name="cannot_find_available_storage">Kullanılabilir depolama alanı yok</string>
|
||||
<string name="rename">Yeniden adlandır</string>
|
||||
<string name="text_delete_local_manga">\"%s\" cihazdan kalıcı olarak silinsin mi\?</string>
|
||||
<string name="clear_search_history">Arama geçmişini temizle</string>
|
||||
</resources>
|
||||
10
app/src/main/res/values-v23/themes.xml
Normal file
10
app/src/main/res/values-v23/themes.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V23.Kotatsu" parent="Base.Theme.Kotatsu">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Kotatsu" parent="Base.V23.Kotatsu" />
|
||||
|
||||
</resources>
|
||||
@@ -2,7 +2,8 @@
|
||||
<resources>
|
||||
|
||||
<style name="Base.V27.Kotatsu" parent="Base.Theme.Kotatsu">
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@color/navigation_bar_scrim</item>
|
||||
<item name="android:windowLightNavigationBar">@bool/light_navigation_bar</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -24,4 +24,9 @@
|
||||
<item>@string/detailed_list</item>
|
||||
<item>@string/grid</item>
|
||||
</string-array>
|
||||
<string-array name="screenshots_policy">
|
||||
<item>@string/screenshots_allow</item>
|
||||
<item>@string/screenshots_block_nsfw</item>
|
||||
<item>@string/screenshots_block_all</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -20,4 +20,9 @@
|
||||
<string-array name="values_track_sources_default" translatable="false">
|
||||
<item>favourites</item>
|
||||
</string-array>
|
||||
<string-array name="values_screenshots_policy" translatable="false">
|
||||
<item>allow</item>
|
||||
<item>block_nsfw</item>
|
||||
<item>block_all</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -7,14 +7,9 @@
|
||||
<!-- Navigation -->
|
||||
<dimen name="nav_header_logo_size">36dp</dimen>
|
||||
<dimen name="nav_item_horizontal_padding">24dp</dimen>
|
||||
<!-- Intrinsic height may vary. Use a large radius to ensure a semicircle. -->
|
||||
<dimen name="nav_item_background_corner_radius_left">0dp</dimen>
|
||||
<dimen name="nav_item_background_corner_radius_right">100dp</dimen>
|
||||
<dimen name="nav_item_background_inset_left">0dp</dimen>
|
||||
<dimen name="nav_item_background_inset_right">8dp</dimen>>
|
||||
|
||||
<dimen name="grid_spacing">4dp</dimen>
|
||||
<dimen name="list_spacing">4dp</dimen>
|
||||
<dimen name="grid_spacing">8dp</dimen>
|
||||
<dimen name="list_spacing">8dp</dimen>
|
||||
<dimen name="grid_spacing_outer">2dp</dimen>
|
||||
<dimen name="manga_list_item_height">86dp</dimen>
|
||||
<dimen name="manga_list_details_item_height">120dp</dimen>
|
||||
@@ -26,6 +21,8 @@
|
||||
<dimen name="list_footer_height_outer">48dp</dimen>
|
||||
<dimen name="screen_padding">16dp</dimen>
|
||||
<dimen name="feed_dividers_offset">72dp</dimen>
|
||||
<dimen name="manga_badge_offset_horizontal">4dp</dimen>
|
||||
<dimen name="manga_badge_offset_vertical">2dp</dimen>
|
||||
|
||||
<!--Text dimens-->
|
||||
<dimen name="text_size_h1">22sp</dimen>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources>
|
||||
|
||||
<integer name="search_animation_duration">@android:integer/config_shortAnimTime</integer>
|
||||
|
||||
<integer name="manga_badge_max_character_count">3</integer>
|
||||
</resources>
|
||||
@@ -251,12 +251,16 @@
|
||||
<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="importing_progress">Importing manga: %1$d of %2$d</string>
|
||||
<string name="suggestions">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="screenshots_policy">Screenshots policy</string>
|
||||
<string name="screenshots_allow">Allow</string>
|
||||
<string name="screenshots_block_nsfw">Block on NSFW</string>
|
||||
<string name="screenshots_block_all">Block always</string>
|
||||
<string name="suggestions">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>
|
||||
@@ -137,6 +137,12 @@
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<!-- Shapes -->
|
||||
|
||||
<style name="ShapeAppearanceOverlay.Kotatsu.Cover" parent="">
|
||||
<item name="cornerSize">12dp</item>
|
||||
</style>
|
||||
|
||||
<!--Preferences-->
|
||||
|
||||
<style name="PreferenceThemeOverlay.Kotatsu">
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<!-- Themes -->
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="M">@bool/light_status_bar</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@color/surface_amoled</item>
|
||||
<item name="android:navigationBarColor">@color/surface_amoled</item>
|
||||
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">@null</item>
|
||||
<item name="android:enforceNavigationBarContrast" tools:targetApi="Q">false</item>
|
||||
@@ -66,7 +66,7 @@
|
||||
<item name="appBarLayoutStyle">@style/Widget.Material3.AppBarLayout</item>
|
||||
<item name="tabStyle">@style/Widget.Kotatsu.Tabs</item>
|
||||
<item name="switchStyle">@style/Widget.Kotatsu.Switch</item>
|
||||
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
|
||||
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Filled</item>
|
||||
<item name="recyclerViewStyle">@style/Widget.Kotatsu.RecyclerView</item>
|
||||
|
||||
<!-- Preference text appearance -->
|
||||
@@ -85,4 +85,4 @@
|
||||
|
||||
<style name="ThemeOverlay.Kotatsu.AMOLED" parent="" />
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -38,4 +38,13 @@
|
||||
android:title="@string/show_pages_numbers"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/screenshots_policy"
|
||||
android:entryValues="@array/values_screenshots_policy"
|
||||
android:key="screenshots_policy"
|
||||
android:title="@string/screenshots_policy"
|
||||
app:defaultValue="allow"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user