Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import com.sameerasw.essentials.island.remote.LocalRemotePaused
import com.sameerasw.essentials.island.remote.RemoteExpanded
import com.sameerasw.essentials.island.ui.SurfaceBackdrop
import android.Manifest
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.PowerManager
import androidx.compose.runtime.DisposableEffect
import android.content.Intent
import android.content.pm.PackageManager
import android.content.ContentUris
import androidx.compose.foundation.Image
Expand Down Expand Up @@ -75,7 +78,6 @@ import com.sameerasw.essentials.island.plugins.soften
import com.sameerasw.essentials.island.ui.IslandHaptics
import com.sameerasw.essentials.translation.TranslatableText
import kotlinx.coroutines.launch
import android.os.BatteryManager
import android.os.Build
import com.sameerasw.essentials.utils.BluetoothBatteryUtils
import com.sameerasw.essentials.utils.BluetoothDeviceIcons
Expand Down Expand Up @@ -123,6 +125,10 @@ import com.sameerasw.essentials.island.plugins.BaseIslandPlugin
import com.sameerasw.essentials.island.ui.IslandTextStyles
import com.sameerasw.essentials.island.ui.islandTextColor
import com.sameerasw.essentials.island.ui.components.BatteryGlyph
import com.sameerasw.essentials.island.plugins.timebattery.BatteryStatus
import com.sameerasw.essentials.island.plugins.timebattery.batteryIdleColor
import com.sameerasw.essentials.island.plugins.timebattery.batteryStateColor
import com.sameerasw.essentials.island.plugins.timebattery.readBatteryStatus
import com.sameerasw.essentials.island.ui.components.BatteryRing
import com.sameerasw.essentials.island.ui.components.CameraRow
import com.sameerasw.essentials.utils.CalendarEventUtil
Expand Down Expand Up @@ -442,7 +448,18 @@ private fun BriefOverview(
now = System.currentTimeMillis()
}
}
val battery = remember(now) { readBattery(context) }
var batteryState by remember { mutableStateOf<BatteryStatus?>(null) }
DisposableEffect(context) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
batteryState = readBatteryStatus(context, if (intent.action == Intent.ACTION_BATTERY_CHANGED) intent else null)
}
}
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply { addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) }
val sticky = context.registerReceiver(receiver, filter)
batteryState = readBatteryStatus(context, sticky)
onDispose { context.unregisterReceiver(receiver) }
}
val alarm = remember(now, alarmHours) { alarmHours?.let { NextAlarm.within(context, it) } }
val devices by produceState(BriefDevicesCache.devices, now) {
val fresh = withContext(Dispatchers.IO) { readDevices(context) }
Expand All @@ -457,8 +474,10 @@ private fun BriefOverview(
}
}

val battery = batteryState
val timeSettings = remember { SettingsRepository(context) }
val time = IslandTimeFormat.format(context, timeSettings, now)
val batteryColor = battery?.let { batteryStateColor(timeSettings, it) ?: batteryIdleColor(timeSettings) } ?: accent
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "EEEMMMd"), Locale.getDefault()).format(Date(now))

val artwork = remember(media?.artwork) { media?.artwork?.asImageBitmap() }
Expand All @@ -483,11 +502,11 @@ private fun BriefOverview(
Spacer(Modifier.width(8.dp))
}
}
if (battery >= 0) {
if (battery != null) {
if (iconStyle) {
BatteryGlyph(battery, accent, showLevel = true)
BatteryGlyph(battery.level, batteryColor, showLevel = true, charging = battery.charging, powerSave = battery.powerSave, limited = battery.limited)
} else {
BatteryRing(battery, accent, showLevel = true)
BatteryRing(battery.level, batteryColor, showLevel = true, charging = battery.charging, powerSave = battery.powerSave, limited = battery.limited)
}
}
},
Expand Down Expand Up @@ -617,13 +636,6 @@ private fun BriefEventRow(event: UpcomingCalendarEvent, now: Long, accent: Color
}
}

private fun readBattery(context: Context): Int {
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return -1
val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
return if (level < 0 || scale <= 0) -1 else level * 100 / scale
}

@Composable
private fun BriefPlayer(media: MediaSnapshot, artwork: ImageBitmap?, onClick: () -> Unit, modifier: Modifier = Modifier) {
val context = LocalContext.current
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.sameerasw.essentials.island.plugins.timebattery

import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color as AndroidColor
import android.os.BatteryManager
import android.os.PowerManager
import androidx.compose.ui.graphics.Color
import com.sameerasw.essentials.data.repository.SettingsRepository
import com.sameerasw.essentials.island.plugins.soften
import com.sameerasw.essentials.utils.battery.ChargeLimitUtil

data class BatteryStatus(
val level: Int,
val charging: Boolean,
val limited: Boolean,
val powerSave: Boolean,
)

private val AUTO_CHARGING = AndroidColor.rgb(0, 230, 118)

fun readBatteryStatus(context: Context, intent: Intent? = null): BatteryStatus? {
val batteryIntent = intent ?: context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return null
val raw = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
if (raw < 0 || scale <= 0) return null
val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
val level = (raw * 100f / scale).toInt()
val plugged = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
val limited = ChargeLimitUtil.isShielded(context, level, plugged)
return BatteryStatus(
level = level,
charging = plugged && !limited,
limited = limited,
powerSave = (context.getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isPowerSaveMode ?: false,
)
}

fun batteryStateColor(settings: SettingsRepository, status: BatteryStatus): Color? {
fun parse(hex: String, fallback: Int) = try {
AndroidColor.parseColor(hex)
} catch (_: Exception) {
fallback
}
val charge = settings.getDuoBatteryChargingColor()
val level = status.level
val raw = when {
status.charging && settings.isDuoBatteryChargingColorEnabled() ->
if (charge.equals("auto", ignoreCase = true)) AUTO_CHARGING else parse(charge, AUTO_CHARGING)
status.powerSave && settings.isDuoBatteryPowerSaveColorEnabled() ->
parse(settings.getDuoBatteryPowerSaveColor(), AndroidColor.rgb(255, 152, 0))
level in 0..settings.getDuoBatteryCriticalLevel() && settings.isDuoBatteryCriticalColorEnabled() ->
parse(settings.getDuoBatteryCriticalColor(), AndroidColor.rgb(244, 67, 54))
level in 0..settings.getDuoBatteryLowLevel() && settings.isDuoBatteryLowColorEnabled() ->
parse(settings.getDuoBatteryLowColor(), AndroidColor.rgb(255, 235, 59))
else -> return null
}
return Color(soften(raw))
}

fun batteryIdleColor(settings: SettingsRepository): Color? {
if (!settings.isIslandBatteryIdleColorEnabled()) return null
val raw = try {
AndroidColor.parseColor(settings.getIslandBatteryIdleColor())
} catch (_: Exception) {
AndroidColor.WHITE
}
return Color(soften(raw))
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color as AndroidColor
import android.os.BatteryManager
import android.provider.AlarmClock
import android.os.PowerManager
import android.text.format.DateFormat
Expand All @@ -35,7 +33,6 @@ import com.sameerasw.essentials.island.model.IslandPriority
import com.sameerasw.essentials.island.model.LineContent
import com.sameerasw.essentials.island.model.PluginRequest
import com.sameerasw.essentials.island.plugins.BaseIslandPlugin
import com.sameerasw.essentials.island.plugins.soften
import com.sameerasw.essentials.island.ui.components.BatteryGlyph
import com.sameerasw.essentials.island.ui.components.BatteryRing
import com.sameerasw.essentials.island.ui.components.IslandIcon
Expand Down Expand Up @@ -203,16 +200,11 @@ class TimeBatteryPlugin : BaseIslandPlugin() {
}

private fun readBattery(intent: Intent?) {
val batteryIntent = intent ?: context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return
val raw = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
if (raw < 0 || scale <= 0) return
val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
level = (raw * 100f / scale).toInt()
val plugged = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
shielded = com.sameerasw.essentials.utils.battery.ChargeLimitUtil.isShielded(context, level, plugged)
charging = plugged && !shielded
powerSave = (context.getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isPowerSaveMode ?: false
val status = readBatteryStatus(context, intent) ?: return
level = status.level
shielded = status.limited
charging = status.charging
powerSave = status.powerSave
}

private fun severity(): Int = when {
Expand Down Expand Up @@ -307,7 +299,7 @@ class TimeBatteryPlugin : BaseIslandPlugin() {
CompactCell("battery") {
when {
iconStyle -> BatteryGlyph(batteryLevel, displayColor ?: islandAccent(), showLevel = showLevel, charging = isCharging, powerSave = isSaving, limited = isLimited)
else -> BatteryRing(batteryLevel, displayColor ?: islandAccent(), showLevel = showLevel)
else -> BatteryRing(batteryLevel, displayColor ?: islandAccent(), showLevel = showLevel, charging = isCharging, powerSave = isSaving, limited = isLimited)
}
},
),
Expand All @@ -316,8 +308,8 @@ class TimeBatteryPlugin : BaseIslandPlugin() {
LineContent(
icon = {
when {
iconStyle -> BatteryGlyph(batteryLevel, displayColor ?: islandAccent(), showLevel = false)
else -> BatteryRing(batteryLevel, displayColor ?: islandAccent(), showLevel = false)
iconStyle -> BatteryGlyph(batteryLevel, displayColor ?: islandAccent(), showLevel = false, charging = isCharging, powerSave = isSaving, limited = isLimited)
else -> BatteryRing(batteryLevel, displayColor ?: islandAccent(), showLevel = false, charging = isCharging, powerSave = isSaving, limited = isLimited)
}
},
start = "$batteryLevel%",
Expand Down Expand Up @@ -345,39 +337,11 @@ class TimeBatteryPlugin : BaseIslandPlugin() {
}
}

private fun idleColor(): Color? {
if (!settings.isIslandBatteryIdleColorEnabled()) return null
val raw = try {
AndroidColor.parseColor(settings.getIslandBatteryIdleColor())
} catch (_: Exception) {
AndroidColor.WHITE
}
return Color(soften(raw))
}
private fun idleColor(): Color? = batteryIdleColor(settings)

private fun stateColor(): Color? {
fun parse(hex: String, fallback: Int) = try {
AndroidColor.parseColor(hex)
} catch (_: Exception) {
fallback
}
val charge = settings.getDuoBatteryChargingColor()
val raw = when {
charging && settings.isDuoBatteryChargingColorEnabled() ->
if (charge.equals("auto", ignoreCase = true)) AUTO_CHARGING else parse(charge, AUTO_CHARGING)
powerSave && settings.isDuoBatteryPowerSaveColorEnabled() ->
parse(settings.getDuoBatteryPowerSaveColor(), AndroidColor.rgb(255, 152, 0))
level in 0..settings.getDuoBatteryCriticalLevel() && settings.isDuoBatteryCriticalColorEnabled() ->
parse(settings.getDuoBatteryCriticalColor(), AndroidColor.rgb(244, 67, 54))
level in 0..settings.getDuoBatteryLowLevel() && settings.isDuoBatteryLowColorEnabled() ->
parse(settings.getDuoBatteryLowColor(), AndroidColor.rgb(255, 235, 59))
else -> return null
}
return Color(soften(raw))
}
private fun stateColor(): Color? = batteryStateColor(settings, BatteryStatus(level, charging, shielded, powerSave))

private companion object {
const val BATTERY_KEY = "battery"
val AUTO_CHARGING = AndroidColor.rgb(0, 230, 118)
}
}
Loading
Loading