Skip to content

Commit

Permalink
Now displaying last week unlock counts chart on the HomeScreen
Browse files Browse the repository at this point in the history
  • Loading branch information
sweakpl committed Feb 26, 2023
1 parent 0833afb commit 72f8a27
Show file tree
Hide file tree
Showing 14 changed files with 448 additions and 6 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ dependencies {
// noinspection GradleDependency
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'

// View components
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

// Test
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.sweak.unlockmaster.data.local.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.sweak.unlockmaster.data.local.database.entities.ScreenOnEventEntity
import com.sweak.unlockmaster.data.local.database.entities.UnlockEventEntity

@Dao
Expand All @@ -15,7 +14,7 @@ interface UnlockEventsDao {
@Query("SELECT COUNT(*) FROM unlock_event WHERE timeInMillis >= :sinceTimeInMillis")
suspend fun getUnlockEventsCountSinceTime(sinceTimeInMillis: Long): Int

@Query("SELECT * FROM unlock_event WHERE timeInMillis >= :sinceTimeInMillis")
@Query("SELECT * FROM unlock_event WHERE timeInMillis >= :sinceTimeInMillis ORDER BY timeInMillis")
suspend fun getUnlockEventsSinceTime(sinceTimeInMillis: Long): List<UnlockEventEntity>

@Query("SELECT * FROM unlock_event ORDER BY timeInMillis DESC LIMIT 1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,14 @@ class TimeRepositoryImpl : TimeRepository {
.withNano(0)
.toInstant()
.toEpochMilli()

override fun getSixDaysBeforeDayBeginningTimeInMillis(): Long =
ZonedDateTime.now()
.minusDays(6)
.withHour(0)
.withMinute(0)
.withSecond(0)
.withNano(0)
.toInstant()
.toEpochMilli()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ interface TimeRepository {
fun getCurrentTimeInMillis(): Long
fun getTodayBeginningTimeInMillis(): Long
fun getTomorrowBeginningTimeInMillis(): Long
fun getSixDaysBeforeDayBeginningTimeInMillis(): Long
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.sweak.unlockmaster.domain.use_case.unlock_events

import com.sweak.unlockmaster.domain.repository.TimeRepository
import com.sweak.unlockmaster.domain.repository.UnlockEventsRepository
import com.sweak.unlockmaster.presentation.common.util.toTimeInMillis
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import javax.inject.Inject

class GetLastWeekUnlockEventCountsUseCase @Inject constructor(
private val unlockEventsRepository: UnlockEventsRepository,
private val timeRepository: TimeRepository
) {
suspend operator fun invoke(): List<Int> {
val sixDaysBeforeDayBeginningTimeInMillis =
timeRepository.getSixDaysBeforeDayBeginningTimeInMillis()
val lastWeekUnlockEvents = unlockEventsRepository.getUnlockEventsSinceTime(
sinceTimeInMillis = sixDaysBeforeDayBeginningTimeInMillis
)

var intervalBeginningCursorDateTime = ZonedDateTime.ofInstant(
Instant.ofEpochMilli(sixDaysBeforeDayBeginningTimeInMillis),
ZoneId.systemDefault()
)
var intervalEndingCursorDateTime = intervalBeginningCursorDateTime.plusDays(1)

var currentDayUnlockCount = 0
var currentDayIndex = 0
val lastWeekUnlockEventCountsList = mutableListOf(0, 0, 0, 0, 0, 0, 0)
val tomorrowBeginningTimeInMillis = timeRepository.getTomorrowBeginningTimeInMillis()

lastWeekUnlockEvents.forEach {
while (it.timeInMillis < intervalBeginningCursorDateTime.toTimeInMillis() ||
it.timeInMillis >= intervalEndingCursorDateTime.toTimeInMillis()
) {
lastWeekUnlockEventCountsList[currentDayIndex] = currentDayUnlockCount
currentDayUnlockCount = 0
currentDayIndex++

intervalBeginningCursorDateTime = intervalBeginningCursorDateTime.plusDays(1)
intervalEndingCursorDateTime = intervalEndingCursorDateTime.plusDays(1)

if (intervalBeginningCursorDateTime.toTimeInMillis() == tomorrowBeginningTimeInMillis) {
break
}
}

currentDayUnlockCount++
}

lastWeekUnlockEventCountsList[currentDayIndex] = currentDayUnlockCount

return lastWeekUnlockEventCountsList
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sweak.unlockmaster.presentation.common.util

import java.text.SimpleDateFormat
import java.time.ZonedDateTime
import java.util.*

fun ZonedDateTime.toTimeInMillis(): Long =
this.toInstant().toEpochMilli()

fun getShortDayString(timeInMillis: Long): String {
return SimpleDateFormat(
"EEE",
Locale.getDefault()
).format(timeInMillis)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.sweak.unlockmaster.presentation.common.util

import android.graphics.Canvas
import android.graphics.RectF
import com.github.mikephil.charting.animation.ChartAnimator
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.highlight.Range
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet
import com.github.mikephil.charting.renderer.BarChartRenderer
import com.github.mikephil.charting.utils.Transformer
import com.github.mikephil.charting.utils.Utils
import com.github.mikephil.charting.utils.ViewPortHandler
import kotlin.math.abs
import kotlin.math.ceil

class RoundedBarChartRenderer(
chart: BarDataProvider?,
animator: ChartAnimator?,
viewPortHandler: ViewPortHandler?
) : BarChartRenderer(chart, animator, viewPortHandler) {

private val mBarShadowRectBuffer = RectF()

override fun drawDataSet(c: Canvas, dataSet: IBarDataSet, index: Int) {
val trans: Transformer = mChart.getTransformer(dataSet.axisDependency)
mBarBorderPaint.color = dataSet.barBorderColor
mBarBorderPaint.strokeWidth = Utils.convertDpToPixel(dataSet.barBorderWidth)
val drawBorder = dataSet.barBorderWidth > 0f
val phaseX = mAnimator.phaseX
val phaseY = mAnimator.phaseY

// draw the bar shadow before the values
if (mChart.isDrawBarShadowEnabled) {
mShadowPaint.color = dataSet.barShadowColor
val barData = mChart.barData
val barWidth = barData.barWidth
val barWidthHalf = barWidth / 2.0f
var x: Float
var i = 0
val count = ceil((dataSet.entryCount.toFloat() * phaseX).toDouble()).toInt()
.coerceAtMost(dataSet.entryCount)

while (i < count) {
val e = dataSet.getEntryForIndex(i)
x = e.x
mBarShadowRectBuffer.left = x - barWidthHalf
mBarShadowRectBuffer.right = x + barWidthHalf
trans.rectValueToPixel(mBarShadowRectBuffer)

if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) {
i++
continue
}

if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) {
break
}

mBarShadowRectBuffer.top = mViewPortHandler.contentTop()
mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom()
c.drawRoundRect(mBarShadowRectBuffer, 10f, 10f, mShadowPaint)
i++
}
}

// initialize the buffer
if (mBarBuffers == null) {
return
}

val buffer = mBarBuffers[index]
buffer.setPhases(phaseX, phaseY)
buffer.setDataSet(index)
buffer.setInverted(mChart.isInverted(dataSet.axisDependency))
buffer.setBarWidth(mChart.barData.barWidth)
buffer.feed(dataSet)
trans.pointValuesToPixel(buffer.buffer)
val isSingleColor = dataSet.colors.size == 1

if (isSingleColor) {
mRenderPaint.color = dataSet.color
}

var j = 0

while (j < buffer.size()) {
if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
j += 4
continue
}

if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) {
break
}

if (!isSingleColor) {
// Set the color for the currently drawn value. If the index
// is out of bounds, reuse colors.
mRenderPaint.color = dataSet.getColor(j / 4)
}

val rounding = abs(buffer.buffer[j] - buffer.buffer[j + 2]) / 8

c.drawRoundRect(
buffer.buffer[j],
buffer.buffer[j + 1],
buffer.buffer[j + 2],
buffer.buffer[j + 3],
rounding,
rounding,
mRenderPaint
)

if (drawBorder) {
val borderRounding = abs(buffer.buffer[j] - buffer.buffer[j + 2]) / 8
c.drawRoundRect(
buffer.buffer[j],
buffer.buffer[j + 1],
buffer.buffer[j + 2],
buffer.buffer[j + 3],
borderRounding,
borderRounding,
mBarBorderPaint
)
}

j += 4
}
}

override fun drawHighlighted(c: Canvas, indices: Array<Highlight>) {
val barData = mChart.barData

for (high in indices) {
val set = barData.getDataSetByIndex(high.dataSetIndex)

if (set == null || !set.isHighlightEnabled) {
continue
}

val e = set.getEntryForXValue(high.x, high.y)

if (!isInBoundsX(e, set)) {
continue
}

val trans: Transformer = mChart.getTransformer(set.axisDependency)
mHighlightPaint.color = set.highLightColor
mHighlightPaint.alpha = set.highLightAlpha
val isStack = high.stackIndex >= 0 && e.isStacked
val y1: Float
val y2: Float

if (isStack) {
if (mChart.isHighlightFullBarEnabled) {
y1 = e.positiveSum
y2 = -e.negativeSum
} else {
val range: Range = e.ranges[high.stackIndex]
y1 = range.from
y2 = range.to
}
} else {
y1 = e.y
y2 = 0f
}

prepareBarHighlight(e.x, y1, y2, barData.barWidth / 2f, trans)
setHighlightDrawPos(high, mBarRect)
val rounding = abs(mBarRect.left - mBarRect.right) / 8
c.drawRoundRect(mBarRect, rounding, rounding, mHighlightPaint)
}
}
}
Loading

0 comments on commit 72f8a27

Please sign in to comment.