diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt index 95ff882d6..7d590311d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt @@ -9,6 +9,8 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.os.BatteryManager import android.util.AttributeSet import android.view.View import android.view.WindowInsets @@ -16,6 +18,7 @@ import androidx.annotation.AttrRes import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes import androidx.core.graphics.ColorUtils +import androidx.core.graphics.withScale import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import org.koitharu.kotatsu.R @@ -29,6 +32,7 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import com.google.android.material.R as materialR + class ReaderInfoBarView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -38,7 +42,7 @@ class ReaderInfoBarView @JvmOverloads constructor( private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val textBounds = Rect() private val timeFormat = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) - private val timeReceiver = TimeReceiver() + private val systemStateReceiver = SystemStateReceiver() private var insetLeft: Int = 0 private var insetRight: Int = 0 private var insetTop: Int = 0 @@ -52,8 +56,10 @@ class ReaderInfoBarView @JvmOverloads constructor( context.getThemeColor(materialR.attr.colorSurface, Color.WHITE), 200, ) + private val batteryIcon = ContextCompat.getDrawable(context, R.drawable.ic_battery_outline) private var timeText = timeFormat.format(LocalTime.now()) + private var batteryText = "" private var text: String = "" private var prevTextHeight: Int = 0 @@ -99,7 +105,8 @@ class ReaderInfoBarView @JvmOverloads constructor( override fun onDraw(canvas: Canvas) { super.onDraw(canvas) computeTextHeight() - val ty = innerHeight / 2f + textBounds.height() / 2f - textBounds.bottom + val h = innerHeight.toFloat() + val ty = h / 2f + textBounds.height() / 2f - textBounds.bottom paint.textAlign = Paint.Align.LEFT canvas.drawTextOutline( text, @@ -108,11 +115,26 @@ class ReaderInfoBarView @JvmOverloads constructor( ) if (isTimeVisible) { paint.textAlign = Paint.Align.RIGHT - canvas.drawTextOutline( - timeText, - (width - paddingRight - insetRight - cutoutInsetRight).toFloat(), - paddingTop + insetTop + ty, - ) + var endX = (width - paddingRight - insetRight - cutoutInsetRight).toFloat() + canvas.drawTextOutline(timeText, endX, paddingTop + insetTop + ty) + if (batteryText.isNotEmpty()) { + paint.getTextBounds(timeText, 0, timeText.length, textBounds) + endX -= textBounds.width() + endX -= h * 0.6f + canvas.drawTextOutline(batteryText, endX, paddingTop + insetTop + ty) + batteryIcon?.let { + paint.getTextBounds(batteryText, 0, batteryText.length, textBounds) + endX -= textBounds.width() + val iconCenter = paddingTop + insetTop + textBounds.height() / 2 + it.setBounds( + (endX - h).toInt(), + (iconCenter - h / 2).toInt(), + endX.toInt(), + (iconCenter + h / 2).toInt(), + ) + it.drawWithOutline(canvas) + } + } } } @@ -130,8 +152,11 @@ class ReaderInfoBarView @JvmOverloads constructor( super.onAttachedToWindow() ContextCompat.registerReceiver( context, - timeReceiver, - IntentFilter(Intent.ACTION_TIME_TICK), + systemStateReceiver, + IntentFilter().apply { + addAction(Intent.ACTION_TIME_TICK) + addAction(Intent.ACTION_BATTERY_CHANGED) + }, ContextCompat.RECEIVER_EXPORTED, ) updateCutoutInsets(ViewCompat.getRootWindowInsets(this)) @@ -139,7 +164,7 @@ class ReaderInfoBarView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() - context.unregisterReceiver(timeReceiver) + context.unregisterReceiver(systemStateReceiver) } fun update(state: ReaderUiState?) { @@ -167,7 +192,7 @@ class ReaderInfoBarView @JvmOverloads constructor( } private fun computeTextHeight(): Int { - val str = text + timeText + val str = text + batteryText + timeText paint.getTextBounds(str, 0, str.length, textBounds) return textBounds.height() } @@ -181,6 +206,20 @@ class ReaderInfoBarView @JvmOverloads constructor( drawText(text, x, y, paint) } + private fun Drawable.drawWithOutline(canvas: Canvas) { + var requiredScale = (bounds.width() + paint.strokeWidth * 2f) / bounds.width().toFloat() + setTint(colorOutline) + canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) { + draw(canvas) + } + requiredScale = 1f / requiredScale + canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) { + draw(canvas) + } + setTint(colorText) + draw(canvas) + } + private fun updateCutoutInsets(insetsCompat: WindowInsetsCompat?) { val cutouts = (insetsCompat ?: return).displayCutout?.boundingRects.orEmpty() cutoutInsetLeft = 0 @@ -195,9 +234,15 @@ class ReaderInfoBarView @JvmOverloads constructor( } } - private inner class TimeReceiver : BroadcastReceiver() { + private inner class SystemStateReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) + val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) + if (level != -1 && scale != -1) { + batteryText = context.getString(R.string.percent_string_pattern, (level * 100 / scale).toString()) + } - override fun onReceive(context: Context?, intent: Intent?) { timeText = timeFormat.format(LocalTime.now()) if (isTimeVisible) { invalidate() diff --git a/app/src/main/res/drawable/ic_battery_outline.xml b/app/src/main/res/drawable/ic_battery_outline.xml new file mode 100644 index 000000000..74bbf926d --- /dev/null +++ b/app/src/main/res/drawable/ic_battery_outline.xml @@ -0,0 +1,10 @@ + + +