Stats activity
This commit is contained in:
@@ -239,6 +239,9 @@
|
||||
<data android:scheme="kotatsu+kitsu" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.stats.ui.StatsActivity"
|
||||
android:label="@string/reading_stats" />
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||
|
||||
@@ -10,6 +10,7 @@ data class ReadingTime(
|
||||
) {
|
||||
|
||||
fun format(resources: Resources): String = when {
|
||||
hours == 0 && minutes == 0 -> resources.getString(R.string.less_than_minute)
|
||||
hours == 0 -> resources.getQuantityString(R.plurals.minutes, minutes, minutes)
|
||||
minutes == 0 -> resources.getQuantityString(R.plurals.hours, hours, hours)
|
||||
else -> resources.getString(
|
||||
|
||||
@@ -11,7 +11,9 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkManageIntent
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver
|
||||
@@ -27,6 +29,7 @@ class HistoryListFragment : MangaListFragment() {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
RecyclerScrollKeeper(binding.recyclerView).attach()
|
||||
addMenuProvider(HistoryListMenuProvider(binding.root.context, viewModel))
|
||||
viewModel.isStatsEnabled.observe(viewLifecycleOwner, MenuInvalidator(requireActivity()))
|
||||
}
|
||||
|
||||
override fun onScrolledToEnd() = Unit
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.history.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
@@ -9,6 +10,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.dialog.RememberSelectionDialogListener
|
||||
import org.koitharu.kotatsu.core.util.ext.DIALOG_THEME_CENTERED
|
||||
import org.koitharu.kotatsu.stats.ui.StatsActivity
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
@@ -24,6 +26,11 @@ class HistoryListMenuProvider(
|
||||
menuInflater.inflate(R.menu.opt_history, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
super.onPrepareMenu(menu)
|
||||
menu.findItem(R.id.action_stats)?.isVisible = viewModel.isStatsEnabled.value
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_clear_history -> {
|
||||
@@ -31,6 +38,11 @@ class HistoryListMenuProvider(
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_stats -> {
|
||||
context.startActivity(Intent(context, StatsActivity::class.java))
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,12 @@ class HistoryListViewModel @Inject constructor(
|
||||
g && s.isGroupingSupported()
|
||||
}
|
||||
|
||||
val isStatsEnabled = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_STATS_ENABLED,
|
||||
valueProducer = { isStatsEnabled },
|
||||
)
|
||||
|
||||
override val content = combine(
|
||||
sortOrder.flatMapLatest { repository.observeAllWithHistory(it) },
|
||||
isGroupingEnabled,
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.koitharu.kotatsu.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class StatsSettingsFragment : BasePreferenceFragment(R.string.reading_stats) {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_stats)
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ interface StatsDao {
|
||||
@Query("SELECT IFNULL(SUM(duration), 0) FROM stats")
|
||||
suspend fun getTotalReadingTime(): Long
|
||||
|
||||
@Query("SELECT manga_id, SUM(duration) AS d FROM stats GROUP BY manga_id ORDER BY d")
|
||||
@Query("SELECT manga_id, SUM(duration) AS d FROM stats GROUP BY manga_id ORDER BY d DESC")
|
||||
suspend fun getDurationStats(): Map<@MapColumn("manga_id") Long, @MapColumn("d") Long>
|
||||
|
||||
@Upsert
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.room.withTransaction
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
import org.koitharu.kotatsu.stats.domain.StatsRecord
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class StatsRepository @Inject constructor(
|
||||
@@ -12,14 +13,23 @@ class StatsRepository @Inject constructor(
|
||||
|
||||
suspend fun getReadingStats(): List<StatsRecord> = db.withTransaction {
|
||||
val stats = db.getStatsDao().getDurationStats()
|
||||
val minute = TimeUnit.MINUTES.toMillis(1)
|
||||
val mangaDao = db.getMangaDao()
|
||||
val result = ArrayList<StatsRecord>(stats.size)
|
||||
var other = StatsRecord(null, 0)
|
||||
for ((mangaId, duration) in stats) {
|
||||
val manga = mangaDao.find(mangaId)?.toManga() ?: continue
|
||||
result += StatsRecord(
|
||||
manga = manga,
|
||||
duration = duration,
|
||||
)
|
||||
val manga = mangaDao.find(mangaId)?.toManga()
|
||||
if (manga == null || duration < minute) {
|
||||
other = other.copy(duration = other.duration + duration)
|
||||
} else {
|
||||
result += StatsRecord(
|
||||
manga = manga,
|
||||
duration = duration,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (other.duration != 0L) {
|
||||
result += other
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.stats.domain
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.R
|
||||
import com.google.android.material.color.MaterialColors
|
||||
@@ -13,7 +14,7 @@ import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
data class StatsRecord(
|
||||
val manga: Manga,
|
||||
val manga: Manga?,
|
||||
val duration: Long,
|
||||
) : ListModel {
|
||||
|
||||
@@ -34,8 +35,12 @@ data class StatsRecord(
|
||||
|
||||
@ColorInt
|
||||
fun getColor(context: Context): Int {
|
||||
val hue = (manga.id.absoluteValue % 360).toFloat()
|
||||
val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
|
||||
val color = if (manga != null) {
|
||||
val hue = (manga.id.absoluteValue % 360).toFloat()
|
||||
ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
|
||||
} else {
|
||||
context.getThemeColor(R.attr.colorSurface)
|
||||
}
|
||||
val backgroundColor = context.getThemeColor(R.attr.colorSurfaceContainerHigh)
|
||||
return MaterialColors.harmonize(color, backgroundColor)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.stats.ui
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.databinding.ItemStatsBinding
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -14,12 +15,13 @@ fun statsAD(
|
||||
) {
|
||||
|
||||
binding.root.setOnClickListener { v ->
|
||||
listener.onItemClick(item.manga, v)
|
||||
listener.onItemClick(item.manga ?: return@setOnClickListener, v)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.manga.title
|
||||
binding.textViewTitle.text = item.manga?.title ?: getString(R.string.other_manga)
|
||||
binding.textViewSummary.text = item.time.format(context.resources)
|
||||
binding.imageViewBadge.imageTintList = ColorStateList.valueOf(item.getColor(context))
|
||||
binding.root.isClickable = item.manga != null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,17 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.FragmentStatsBinding
|
||||
import org.koitharu.kotatsu.databinding.ActivityStatsBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -19,28 +22,26 @@ import org.koitharu.kotatsu.stats.domain.StatsRecord
|
||||
import org.koitharu.kotatsu.stats.ui.views.PieChartView
|
||||
|
||||
@AndroidEntryPoint
|
||||
class StatsFragment : BaseFragment<FragmentStatsBinding>(), OnListItemClickListener<Manga> {
|
||||
class StatsActivity : BaseActivity<ActivityStatsBinding>(), OnListItemClickListener<Manga> {
|
||||
|
||||
private val viewModel: StatsViewModel by viewModels()
|
||||
|
||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentStatsBinding {
|
||||
return FragmentStatsBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewBindingCreated(binding: FragmentStatsBinding, savedInstanceState: Bundle?) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityStatsBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val adapter = BaseListAdapter<StatsRecord>()
|
||||
.addDelegate(ListItemType.FEED, statsAD(this))
|
||||
binding.recyclerView.adapter = adapter
|
||||
viewModel.readingStats.observe(viewLifecycleOwner) {
|
||||
viewBinding.recyclerView.adapter = adapter
|
||||
viewModel.readingStats.observe(this) {
|
||||
val sum = it.sumOf { it.duration }
|
||||
binding.chart.setData(
|
||||
viewBinding.chart.setData(
|
||||
it.map { v ->
|
||||
PieChartView.Segment(
|
||||
value = (v.duration / 1000).toInt(),
|
||||
label = v.manga.title,
|
||||
label = v.manga?.title ?: getString(R.string.other_manga),
|
||||
percent = (v.duration.toDouble() / sum).toFloat(),
|
||||
color = v.getColor(binding.chart.context),
|
||||
color = v.getColor(this),
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.stats.ui.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
@@ -9,28 +10,37 @@ import android.graphics.PorterDuffXfermode
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Xfermode
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.collection.MutableIntList
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.minus
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.parsers.util.replaceWith
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sqrt
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
class PieChartView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
) : View(context, attrs, defStyleAttr), GestureDetector.OnGestureListener {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val segments = ArrayList<Segment>()
|
||||
private val chartBounds = RectF()
|
||||
private val clearColor = context.getThemeColor(android.R.attr.colorBackground)
|
||||
private val touchDetector = GestureDetectorCompat(context, this)
|
||||
private var hightlightedSegment = -1
|
||||
|
||||
var onSegmentClickListener: OnSegmentClickListener? = null
|
||||
|
||||
init {
|
||||
touchDetector.setIsLongpressEnabled(false)
|
||||
paint.strokeWidth = context.resources.resolveDp(2f)
|
||||
}
|
||||
|
||||
@@ -39,6 +49,9 @@ class PieChartView @JvmOverloads constructor(
|
||||
var angle = 0f
|
||||
for ((i, segment) in segments.withIndex()) {
|
||||
paint.color = segment.color
|
||||
if (i == hightlightedSegment) {
|
||||
paint.color = ColorUtils.setAlphaComponent(paint.color, 180)
|
||||
}
|
||||
paint.style = Paint.Style.FILL
|
||||
val sweepAngle = segment.percent * 360f
|
||||
canvas.drawArc(
|
||||
@@ -75,15 +88,80 @@ class PieChartView @JvmOverloads constructor(
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (event.actionMasked == MotionEvent.ACTION_CANCEL || event.actionMasked == MotionEvent.ACTION_UP) {
|
||||
hightlightedSegment = -1
|
||||
invalidate()
|
||||
}
|
||||
return super.onTouchEvent(event) || touchDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
val segment = findSegmentIndex(e.x, e.y)
|
||||
if (segment != hightlightedSegment) {
|
||||
hightlightedSegment = segment
|
||||
invalidate()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShowPress(e: MotionEvent) = Unit
|
||||
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
onSegmentClickListener?.run {
|
||||
val segment = segments.getOrNull(findSegmentIndex(e.x, e.y))
|
||||
if (segment != null) {
|
||||
onSegmentClick(this@PieChartView, segment)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean = false
|
||||
|
||||
override fun onLongPress(e: MotionEvent) = Unit
|
||||
|
||||
override fun onFling(e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean = false
|
||||
|
||||
fun setData(value: List<Segment>) {
|
||||
segments.replaceWith(value)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun findSegmentIndex(x: Float, y: Float): Int {
|
||||
val dy = (y - chartBounds.centerY()).toDouble()
|
||||
val dx = (x - chartBounds.centerX()).toDouble()
|
||||
val distance = sqrt(dx * dx + dy * dy).toFloat()
|
||||
if (distance < chartBounds.height() / 4f || distance > chartBounds.centerX()) {
|
||||
return -1
|
||||
}
|
||||
var touchAngle = Math.toDegrees(Math.atan2(dy, dx)).toFloat()
|
||||
if (touchAngle < 0) {
|
||||
touchAngle += 360
|
||||
}
|
||||
var angle = 0f
|
||||
for ((i, segment) in segments.withIndex()) {
|
||||
val sweepAngle = segment.percent * 360f
|
||||
if (touchAngle in angle..(angle + sweepAngle)) {
|
||||
return i
|
||||
}
|
||||
angle += sweepAngle
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
class Segment(
|
||||
val value: Int,
|
||||
val label: String,
|
||||
val percent: Float,
|
||||
val color: Int,
|
||||
)
|
||||
|
||||
interface OnSegmentClickListener {
|
||||
|
||||
fun onSegmentClick(view: PieChartView, segment: Segment)
|
||||
}
|
||||
}
|
||||
|
||||
54
app/src/main/res/layout/activity_stats.xml
Normal file
54
app/src/main/res/layout/activity_stats.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="noScroll">
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<org.koitharu.kotatsu.stats.ui.views.PieChartView
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="24dp"
|
||||
app:layout_constraintDimensionRatio="1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/chart"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_stats" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
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="match_parent"
|
||||
android:background="?android:colorBackground"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.koitharu.kotatsu.stats.ui.views.PieChartView
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="24dp"
|
||||
app:layout_constraintDimensionRatio="1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="24dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/chart"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_stats" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -17,6 +17,7 @@
|
||||
android:id="@+id/imageView_badge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
app:srcCompat="@drawable/bg_rounded_square" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
android:title="@string/clear_history"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_stats"
|
||||
android:orderInCategory="40"
|
||||
android:title="@string/statistics"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -605,4 +605,7 @@
|
||||
<string name="multiple_cbz_files">Multiple CBZ files</string>
|
||||
<string name="stats_enabled">Enable statistics</string>
|
||||
<string name="reading_stats">Reading statistics</string>
|
||||
<string name="other_manga">Other manga</string>
|
||||
<string name="less_than_minute">Less than a minute</string>
|
||||
<string name="statistics">Statistics</string>
|
||||
</resources>
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
android:summary="@string/related_manga_summary"
|
||||
android:title="@string/related_manga" />
|
||||
|
||||
<Preference
|
||||
android:fragment="org.koitharu.kotatsu.settings.StatsSettingsFragment"
|
||||
android:key="stats"
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="stats_on"
|
||||
android:title="@string/reading_stats"
|
||||
app:allowDividerAbove="true" />
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="stats_on"
|
||||
android:layout="@layout/preference_toggle_header"
|
||||
android:title="@string/stats_enabled" />
|
||||
|
||||
<Preference
|
||||
android:dependency="stats_on"
|
||||
android:fragment="org.koitharu.kotatsu.stats.ui.StatsFragment"
|
||||
android:title="@string/reading_stats" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
Reference in New Issue
Block a user