diff --git a/app/build.gradle b/app/build.gradle
index 66593f25..be2685a1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,6 +15,7 @@ android {
applicationId "com.openvehicles.OVMS"
minSdk 21
targetSdk 36
+ vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index db0dc0ed..3f3d420a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -48,6 +48,7 @@
@@ -72,6 +73,7 @@
23) packStatus.voltRef = result[23].toFloat()
- if (result.size > 26) packStatus.current = result[26].toFloat()
- packStatus.tempAmbient = result[17].toFloat()
- if (result.size > 25) packStatus.tempCharger = result[25].toFloat()
- doors = result[18].toInt() // doors3
+ packStatus.volt = result[21 + shift].toFloat()
+ if (result.size > 23 + shift) packStatus.voltRef = result[23 + shift].toFloat()
+ if (result.size > 26 + shift) packStatus.current = result[26 + shift].toFloat()
+ packStatus.tempAmbient = result[17 + shift].toFloat()
+ if (result.size > 25 + shift) packStatus.tempCharger = result[25 + shift].toFloat()
+ doors = result[18 + shift].toInt() // doors3
packStatus.isCarAwake = doors and 0x01 != 0
- if (result.size > 24) {
- doors = result[24].toInt() // doors5
+ if (result.size > 24 + shift) {
+ doors = result[24 + shift].toInt() // doors5
packStatus.isCharging12V = doors and 0x10 != 0
}
@@ -125,7 +142,7 @@ class AuxBatteryData {
Log.v(TAG, "loading from file: $filename")
return try {
inputStream = context!!.openFileInput(filename)
- val isr = InputStreamReader(inputStream)
+ val isr = InputStreamReader(inputStream, StandardCharsets.UTF_8)
val bufferedReader = BufferedReader(isr)
val sb = StringBuilder()
var line: String?
diff --git a/app/src/main/java/com/openvehicles/OVMS/entities/BatteryData.kt b/app/src/main/java/com/openvehicles/OVMS/entities/BatteryData.kt
index e1c382ae..d191a0e0 100644
--- a/app/src/main/java/com/openvehicles/OVMS/entities/BatteryData.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/entities/BatteryData.kt
@@ -8,8 +8,10 @@ import java.io.BufferedReader
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStreamReader
+import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Date
+import java.util.Locale
import java.util.TimeZone
/**
@@ -32,7 +34,7 @@ class BatteryData {
val json = gson.toJson(this)
return try {
outputStream = context!!.openFileOutput(filename, Context.MODE_PRIVATE)
- outputStream.write(json.toByteArray())
+ outputStream.write(json.toByteArray(StandardCharsets.UTF_8))
outputStream.close()
true
} catch (e: Exception) {
@@ -46,7 +48,7 @@ class BatteryData {
var recCnt: Int
var recType: String
var timeStamp: Date
- val serverTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ val serverTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
serverTime.timeZone = TimeZone.getTimeZone("UTC")
var packStatus: PackStatus? = null
var cells: ArrayList? = null
@@ -83,28 +85,43 @@ class BatteryData {
recNr = result[2].toInt()
recCnt = result[3].toInt()
recType = result[4]
- timeStamp = serverTime.parse(result[5])
+
+ // Robust timestamp parsing:
+ var shift = 0
+ try {
+ timeStamp = serverTime.parse(result[5])!!
+ } catch (e: Exception) {
+ // try combined with next part if next part looks like time:
+ try {
+ timeStamp = serverTime.parse(result[5].trim() + " " + result[6].trim())!!
+ shift = 1
+ } catch (e2: Exception) {
+ Log.w(TAG, "failed to parse timestamp: " + result[5])
+ continue
+ }
+ }
+
Log.v(TAG, "processing recType $recType entryNr $recNr/$recCnt")
if (recType == "RT-BAT-P") {
try {
// create record:
packStatus = PackStatus()
packStatus.timeStamp = timeStamp
- packStatus.voltAlert = result[7].toInt()
- packStatus.tempAlert = result[8].toInt()
- packStatus.soc = result[9].toInt().toFloat() / 100
- packStatus.socMin = result[10].toInt().toFloat() / 100
- packStatus.socMax = result[11].toInt().toFloat() / 100
- packStatus.volt = result[12].toInt().toFloat() / 10
- packStatus.voltMin = result[13].toInt().toFloat() / 10
- packStatus.voltMax = result[14].toInt().toFloat() / 10
- packStatus.temp = result[15].toFloat()
- packStatus.tempMin = result[16].toFloat()
- packStatus.tempMax = result[17].toFloat()
- packStatus.voltDevMax = result[18].toInt().toFloat() / 100
- packStatus.tempDevMax = result[19].toFloat()
- packStatus.maxDrivePwr = (result[20].toInt() * 100).toLong()
- packStatus.maxRecupPwr = (result[21].toInt() * 100).toLong()
+ packStatus.voltAlert = result[7 + shift].toInt()
+ packStatus.tempAlert = result[8 + shift].toInt()
+ packStatus.soc = result[9 + shift].toInt().toFloat() / 100
+ packStatus.socMin = result[10 + shift].toInt().toFloat() / 100
+ packStatus.socMax = result[11 + shift].toInt().toFloat() / 100
+ packStatus.volt = result[12 + shift].toInt().toFloat() / 10
+ packStatus.voltMin = result[13 + shift].toInt().toFloat() / 10
+ packStatus.voltMax = result[14 + shift].toInt().toFloat() / 10
+ packStatus.temp = result[15 + shift].toFloat()
+ packStatus.tempMin = result[16 + shift].toFloat()
+ packStatus.tempMax = result[17 + shift].toFloat()
+ packStatus.voltDevMax = result[18 + shift].toInt().toFloat() / 100
+ packStatus.tempDevMax = result[19 + shift].toFloat()
+ packStatus.maxDrivePwr = (result[20 + shift].toInt() * 100).toLong()
+ packStatus.maxRecupPwr = (result[21 + shift].toInt() * 100).toLong()
// store record:
if (packStatus.volt > 0) packHistory.add(packStatus)
@@ -114,7 +131,7 @@ class BatteryData {
}
} else if (recType == "RT-BAT-C") {
try {
- nrCell = result[6].toInt()
+ nrCell = result[6 + shift].toInt()
if (nrCell > cellCount) cellCount = nrCell
// Pack record(s) complete?
@@ -136,16 +153,16 @@ class BatteryData {
// create new record:
cellStatus = CellStatus()
- cellStatus.voltAlert = result[7].toInt()
- cellStatus.tempAlert = result[8].toInt()
- cellStatus.volt = result[9].toInt().toFloat() / 1000
- cellStatus.voltMin = result[10].toInt().toFloat() / 1000
- cellStatus.voltMax = result[11].toInt().toFloat() / 1000
- cellStatus.voltDevMax = result[12].toInt().toFloat() / 1000
- cellStatus.temp = result[13].toFloat()
- cellStatus.tempMin = result[14].toFloat()
- cellStatus.tempMax = result[15].toFloat()
- cellStatus.tempDevMax = result[16].toFloat()
+ cellStatus.voltAlert = result[7 + shift].toInt()
+ cellStatus.tempAlert = result[8 + shift].toInt()
+ cellStatus.volt = result[9 + shift].toInt().toFloat() / 1000
+ cellStatus.voltMin = result[10 + shift].toInt().toFloat() / 1000
+ cellStatus.voltMax = result[11 + shift].toInt().toFloat() / 1000
+ cellStatus.voltDevMax = result[12 + shift].toInt().toFloat() / 1000
+ cellStatus.temp = result[13 + shift].toFloat()
+ cellStatus.tempMin = result[14 + shift].toFloat()
+ cellStatus.tempMax = result[15 + shift].toFloat()
+ cellStatus.tempDevMax = result[16 + shift].toFloat()
// store record:
cells!![nrCell - 1] = cellStatus
@@ -201,7 +218,7 @@ class BatteryData {
Log.v(TAG, "loading from file: $filename")
return try {
inputStream = context!!.openFileInput(filename)
- val isr = InputStreamReader(inputStream)
+ val isr = InputStreamReader(inputStream, StandardCharsets.UTF_8)
val bufferedReader = BufferedReader(isr)
val sb = StringBuilder()
var line: String?
diff --git a/app/src/main/java/com/openvehicles/OVMS/entities/CarData.kt b/app/src/main/java/com/openvehicles/OVMS/entities/CarData.kt
index aa603887..f46482d1 100644
--- a/app/src/main/java/com/openvehicles/OVMS/entities/CarData.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/entities/CarData.kt
@@ -48,6 +48,10 @@ class CarData : Serializable {
@JvmField
var sel_vehicle_image = "" // The vehicle image (on disk)
@JvmField
+ var sel_vehicle_image_ol = "" // The vehicle top-view image (on disk)
+ @JvmField
+ var sel_vehicle_image_map = "" // The vehicle map icon image (on disk)
+ @JvmField
var car_lastupdated: Date? = null
var server_carsconnected = 0
diff --git a/app/src/main/java/com/openvehicles/OVMS/entities/LogsData.kt b/app/src/main/java/com/openvehicles/OVMS/entities/LogsData.kt
index ba02f65a..1186c5ca 100644
--- a/app/src/main/java/com/openvehicles/OVMS/entities/LogsData.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/entities/LogsData.kt
@@ -9,9 +9,11 @@ import java.io.BufferedReader
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStreamReader
+import java.nio.charset.StandardCharsets
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
+import java.util.Locale
import java.util.TimeZone
/**
@@ -71,7 +73,7 @@ class LogsData {
val json = gson.toJson(this)
return try {
outputStream = context!!.openFileOutput(filename, Context.MODE_PRIVATE)
- outputStream.write(json.toByteArray())
+ outputStream.write(json.toByteArray(StandardCharsets.UTF_8))
outputStream.close()
true
} catch (e: Exception) {
@@ -84,9 +86,8 @@ class LogsData {
var recNr: Int
var recCnt: Int
var recType: String
- var timeStamp: String
var entryNr: Int
- val serverTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ val serverTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
serverTime.timeZone = TimeZone.getTimeZone("UTC")
for (i in 0 until cmdSeries.size()) {
val cmd = cmdSeries[i]
@@ -98,28 +99,40 @@ class LogsData {
recNr = result[2].toInt()
recCnt = result[3].toInt()
recType = result[4]
- timeStamp = result[5]
- entryNr = result[6].toInt()
+
+ // Robust timestamp parsing:
+ var shift = 0
+ var ts: Date? = null
+ try {
+ ts = serverTime.parse(result[5])!!
+ } catch (e: Exception) {
+ // try combined with next part if next part looks like time:
+ try {
+ ts = serverTime.parse(result[5].trim() + " " + result[6].trim())!!
+ shift = 1
+ } catch (e2: Exception) {
+ Log.w(TAG, "failed to parse timestamp: " + result[5])
+ continue
+ }
+ }
+
+ entryNr = result[6 + shift].toInt()
// [7++] = Payload
Log.v(TAG, "processing recType $recType entryNr $entryNr")
if (recType == "RT-ENG-LogKeyTime") {
keyTime = KeyTime()
- keyTime.keyHour = result[7].toInt()
- keyTime.keyMinSec = result[8].toInt()
- try {
- keyTime.timeStamp = serverTime.parse(timeStamp)
- } catch (e: Exception) {
- keyTime.timeStamp = Date()
- }
+ keyTime.keyHour = result[7 + shift].toInt()
+ keyTime.keyMinSec = result[8 + shift].toInt()
+ keyTime.timeStamp = ts
} else if (recType == "RT-ENG-LogAlerts") {
if (entryNr == 0) {
alerts = ArrayList(20)
alertsTime = keyTime
}
val alert = Alert()
- alert.code = result[7]
- alert.description = result[8]
+ alert.code = result[7 + shift]
+ alert.description = result[8 + shift]
alerts.add(alert)
} else if (recType == "RT-ENG-LogFaults") {
if (entryNr == 0) {
@@ -127,15 +140,15 @@ class LogsData {
faultEventsTime = keyTime
}
val event = Event()
- event.code = result[7]
- event.description = result[8]
+ event.code = result[7 + shift]
+ event.description = result[8 + shift]
event.time = KeyTime()
- event.time!!.keyHour = result[9].toInt()
- event.time!!.keyMinSec = result[10].toInt()
+ event.time!!.keyHour = result[9 + shift].toInt()
+ event.time!!.keyMinSec = result[10 + shift].toInt()
event.data = arrayOfNulls(3)
- event.data[0] = result[11]
- event.data[1] = result[12]
- event.data[2] = result[13]
+ event.data[0] = result[11 + shift]
+ event.data[1] = result[12 + shift]
+ event.data[2] = result[13 + shift]
faultEvents.add(event)
} else if (recType == "RT-ENG-LogSystem") {
if (entryNr == 0) {
@@ -143,15 +156,15 @@ class LogsData {
systemEventsTime = keyTime
}
val event = Event()
- event.code = result[7]
- event.description = result[8]
+ event.code = result[7 + shift]
+ event.description = result[8 + shift]
event.time = KeyTime()
- event.time!!.keyHour = result[9].toInt()
- event.time!!.keyMinSec = result[10].toInt()
+ event.time!!.keyHour = result[9 + shift].toInt()
+ event.time!!.keyMinSec = result[10 + shift].toInt()
event.data = arrayOfNulls(3)
- event.data[0] = result[11]
- event.data[1] = result[12]
- event.data[2] = result[13]
+ event.data[0] = result[11 + shift]
+ event.data[1] = result[12 + shift]
+ event.data[2] = result[13 + shift]
systemEvents.add(event)
} else if (recType == "RT-ENG-LogCounts") {
if (entryNr == 0) {
@@ -159,15 +172,15 @@ class LogsData {
countersTime = keyTime
}
val counter = Counter()
- counter.code = result[7]
- counter.description = result[8]
+ counter.code = result[7 + shift]
+ counter.description = result[8 + shift]
counter.lastTime = KeyTime()
- counter.lastTime!!.keyHour = result[9].toInt()
- counter.lastTime!!.keyMinSec = result[10].toInt()
+ counter.lastTime!!.keyHour = result[9 + shift].toInt()
+ counter.lastTime!!.keyMinSec = result[10 + shift].toInt()
counter.firstTime = KeyTime()
- counter.firstTime!!.keyHour = result[11].toInt()
- counter.firstTime!!.keyMinSec = result[12].toInt()
- counter.count = result[13].toInt()
+ counter.firstTime!!.keyHour = result[11 + shift].toInt()
+ counter.firstTime!!.keyMinSec = result[12 + shift].toInt()
+ counter.count = result[13 + shift].toInt()
counters.add(counter)
} else if (recType == "RT-ENG-LogMinMax") {
if (entryNr == 0) {
@@ -175,16 +188,16 @@ class LogsData {
minMaxesTime = keyTime
}
val minMax = MinMax()
- minMax.batteryVoltageMin = result[7].toDouble() / 16
- minMax.batteryVoltageMax = result[8].toDouble() / 16
- minMax.capacitorVoltageMin = result[9].toDouble() / 16
- minMax.capacitorVoltageMax = result[10].toDouble() / 16
- minMax.motorCurrentMin = result[11].toInt()
- minMax.motorCurrentMax = result[12].toInt()
- minMax.motorSpeedMin = result[13].toInt()
- minMax.motorSpeedMax = result[14].toInt()
- minMax.deviceTempMin = result[15].toInt()
- minMax.deviceTempMax = result[16].toInt()
+ minMax.batteryVoltageMin = result[7 + shift].toDouble() / 16
+ minMax.batteryVoltageMax = result[8 + shift].toDouble() / 16
+ minMax.capacitorVoltageMin = result[9 + shift].toDouble() / 16
+ minMax.capacitorVoltageMax = result[10 + shift].toDouble() / 16
+ minMax.motorCurrentMin = result[11 + shift].toInt()
+ minMax.motorCurrentMax = result[12 + shift].toInt()
+ minMax.motorSpeedMin = result[13 + shift].toInt()
+ minMax.motorSpeedMax = result[14 + shift].toInt()
+ minMax.deviceTempMin = result[15 + shift].toInt()
+ minMax.deviceTempMax = result[16 + shift].toInt()
minMaxes.add(minMax)
}
} catch (e: Exception) {
@@ -220,7 +233,7 @@ class LogsData {
Log.v(TAG, "loading from file: $filename")
return try {
inputStream = context!!.openFileInput(filename)
- val isr = InputStreamReader(inputStream)
+ val isr = InputStreamReader(inputStream, StandardCharsets.UTF_8)
val bufferedReader = BufferedReader(isr)
val sb = StringBuilder()
var line: String?
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/ApiActivity.kt b/app/src/main/java/com/openvehicles/OVMS/ui/ApiActivity.kt
index 7301f99e..769a9d54 100755
--- a/app/src/main/java/com/openvehicles/OVMS/ui/ApiActivity.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/ApiActivity.kt
@@ -7,6 +7,7 @@ import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
import com.openvehicles.OVMS.api.ApiObservable.addObserver
import com.openvehicles.OVMS.api.ApiObservable.deleteObserver
import com.openvehicles.OVMS.api.ApiObservable.notifyOnBind
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/AuxBatteryFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/AuxBatteryFragment.kt
index 36281d71..b1624f71 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/AuxBatteryFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/AuxBatteryFragment.kt
@@ -1,5 +1,6 @@
package com.openvehicles.OVMS.ui
+import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Paint
import android.os.Bundle
@@ -14,6 +15,8 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.ColorUtils
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.components.LimitLine
import com.github.mikephil.charting.components.XAxis
@@ -25,6 +28,7 @@ import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
+import com.google.android.material.color.MaterialColors
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.entities.AuxBatteryData
import com.openvehicles.OVMS.entities.AuxBatteryData.Companion.loadFile
@@ -32,6 +36,7 @@ import com.openvehicles.OVMS.entities.CarData
import com.openvehicles.OVMS.entities.CmdSeries
import com.openvehicles.OVMS.ui.utils.ProgressOverlay
import com.openvehicles.OVMS.ui.utils.TimeAxisValueFormatter
+import com.openvehicles.OVMS.ui.utils.Ui
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.CarsStorage.getSelectedCarData
import java.text.SimpleDateFormat
@@ -65,6 +70,15 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
private var carData: CarData? = null
private var cmdSeries: CmdSeries? = null
private var appPrefs: AppPrefs? = null
+
+ private var colorText = Color.WHITE
+ private var colorGrid = Color.GRAY
+ private var colorVolt = COLOR_VOLT
+ private var colorVoltGrid = COLOR_VOLT_GRID
+ private var colorTempAmbient = COLOR_TEMP_AMBIENT
+ private var colorTempCharger = COLOR_TEMP_CHARGER
+ private var colorCurrText = COLOR_CURR_TEXT
+ private var colorCurrGrid = COLOR_CURR_GRID
/**
* Check data model validity
@@ -89,6 +103,21 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
showTemp = true
}
+ // Init theme colors:
+ val ctx = requireContext()
+ colorText = MaterialColors.getColor(ctx, android.R.attr.textColorPrimary, Color.WHITE)
+ colorGrid = ColorUtils.setAlphaComponent(colorText, 40)
+
+ val isNight = (ctx.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ if (!isNight) {
+ colorVolt = Color.parseColor("#669900")
+ colorVoltGrid = ColorUtils.setAlphaComponent(colorVolt, 100)
+ colorTempAmbient = Color.parseColor("#998800")
+ colorTempCharger = Color.parseColor("#996600")
+ colorCurrText = Color.parseColor("#994444")
+ colorCurrGrid = ColorUtils.setAlphaComponent(colorCurrText, 100)
+ }
+
// Setup UI:
val rootView = inflater.inflate(R.layout.fragment_auxbattery, null)
val progressOverlay = createProgressOverlay(rootView, false)
@@ -97,6 +126,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
// Setup Pack history chart:
//
packChart = rootView.findViewById(R.id.auxbattery_chart_pack) as LineChart
+ packChart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
packChart.description.isEnabled = false
packChart.setPinchZoom(false) // = pinch zoom in one direction at a time
packChart.setDrawGridBackground(false)
@@ -115,16 +145,16 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
}
})
xAxis = packChart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.isGranularityEnabled = true
xAxis.setGranularity(60f) // one minute
yAxisLeft = packChart.axisLeft
- yAxisLeft.textColor = COLOR_CURR_TEXT
- yAxisLeft.gridColor = COLOR_CURR_GRID
+ yAxisLeft.textColor = colorCurrText
+ yAxisLeft.gridColor = colorCurrGrid
yAxisLeft.valueFormatter = DefaultAxisValueFormatter(0)
yAxisRight = packChart.axisRight
- yAxisRight.textColor = COLOR_VOLT
- yAxisRight.gridColor = COLOR_VOLT_GRID
+ yAxisRight.textColor = colorVolt
+ yAxisRight.gridColor = colorVoltGrid
yAxisRight.valueFormatter = DefaultAxisValueFormatter(1)
yAxisRight.setGranularity(0.1f)
@@ -343,7 +373,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
if (packStatus.isNewSection(lastStatus)) {
val l = LimitLine(xpos)
l.label = timeFmt.format(packStatus.timeStamp!!)
- l.textColor = Color.WHITE
+ l.textColor = colorText
l.textStyle = Paint.Style.FILL
l.setTextSize(6f)
l.enableDashedLine(3f, 2f, 0f)
@@ -368,7 +398,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
)
packTempAmbientSet = dataSet
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.setColor(COLOR_TEMP_AMBIENT)
+ dataSet.setColor(colorTempAmbient)
dataSet.setLineWidth(3f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -378,7 +408,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
LineDataSet(tempChargerValues, getString(R.string.aux_battery_data_temp_charger))
packTempChargerSet = dataSet
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.setColor(COLOR_TEMP_CHARGER)
+ dataSet.setColor(colorTempCharger)
dataSet.setLineWidth(3f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -404,7 +434,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
dataSet = LineDataSet(voltValues, getString(R.string.aux_battery_data_volt))
packVoltSet = dataSet
dataSet.axisDependency = YAxis.AxisDependency.RIGHT
- dataSet.setColor(COLOR_VOLT)
+ dataSet.setColor(colorVolt)
dataSet.setLineWidth(3f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -455,7 +485,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
// display data sets:
val data = LineData(dataSets)
packData = data
- data.setValueTextColor(Color.WHITE)
+ data.setValueTextColor(colorText)
data.setValueTextSize(9f)
packChart.setData(data)
val xAxis = packChart.xAxis
@@ -469,7 +499,7 @@ class AuxBatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.O
}
xAxis.addLimitLine(l)
}
- packChart.legend.textColor = Color.WHITE
+ packChart.legend.textColor = colorText
packChart.invalidate()
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/BaseFragmentActivity.kt b/app/src/main/java/com/openvehicles/OVMS/ui/BaseFragmentActivity.kt
index a701206c..ef99e158 100755
--- a/app/src/main/java/com/openvehicles/OVMS/ui/BaseFragmentActivity.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/BaseFragmentActivity.kt
@@ -6,6 +6,7 @@ import android.content.Intent
import android.content.res.Configuration.ORIENTATION_UNDEFINED
import android.os.Bundle
import android.widget.FrameLayout
+import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction.TRANSIT_FRAGMENT_FADE
import com.openvehicles.OVMS.R
@@ -19,6 +20,7 @@ class BaseFragmentActivity : ApiActivity() {
get() = supportFragmentManager.findFragmentById(contentId)
public override fun onCreate(savedInstanceState: Bundle?) {
+ WindowCompat.setDecorFitsSystemWindows(window, true)
super.onCreate(savedInstanceState)
if (intent == null) {
@@ -51,6 +53,7 @@ class BaseFragmentActivity : ApiActivity() {
private fun setDefaultContentView() {
val fl = FrameLayout(this)
fl.setId(R.id.content)
+ fl.fitsSystemWindows = true
setContentView(fl)
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/BatteryFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/BatteryFragment.kt
index 4998ebd8..ee54d837 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/BatteryFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/BatteryFragment.kt
@@ -1,5 +1,6 @@
package com.openvehicles.OVMS.ui
+import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Paint
import android.os.Bundle
@@ -16,6 +17,7 @@ import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.appcompat.app.AlertDialog
+import androidx.core.graphics.ColorUtils
import com.github.mikephil.charting.charts.CandleStickChart
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.components.LimitLine
@@ -33,6 +35,7 @@ import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
+import com.google.android.material.color.MaterialColors
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.entities.BatteryData
import com.openvehicles.OVMS.entities.BatteryData.CellStatus
@@ -78,6 +81,16 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
private var carData: CarData? = null
private var cmdSeries: CmdSeries? = null
private lateinit var appPrefs: AppPrefs
+
+ private var colorText = Color.WHITE
+ private var colorGrid = Color.GRAY
+ private var colorSocLine = COLOR_SOC_LINE
+ private var colorSocText = COLOR_SOC_TEXT
+ private var colorSocGrid = COLOR_SOC_GRID
+ private var colorVolt = COLOR_VOLT
+ private var colorVoltGrid = COLOR_VOLT_GRID
+ private var colorTemp = COLOR_TEMP
+ private var colorTempGrid = COLOR_TEMP_GRID
private val isPackValid: Boolean
// Check data model validity
@@ -101,6 +114,22 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
showVolt = true
}
+ // Init theme colors:
+ val ctx = requireContext()
+ colorText = MaterialColors.getColor(ctx, android.R.attr.textColorPrimary, Color.WHITE)
+ colorGrid = ColorUtils.setAlphaComponent(colorText, 40)
+
+ val isNight = (ctx.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ if (!isNight) {
+ colorSocLine = Color.parseColor("#A03344CC")
+ colorSocText = Color.parseColor("#8888CC")
+ colorSocGrid = ColorUtils.setAlphaComponent(colorSocText, 100)
+ colorVolt = Color.parseColor("#669900")
+ colorVoltGrid = ColorUtils.setAlphaComponent(colorVolt, 100)
+ colorTemp = Color.parseColor("#998800")
+ colorTempGrid = ColorUtils.setAlphaComponent(colorTemp, 100)
+ }
+
// Setup UI:
@@ -112,6 +141,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
// Setup Cell status chart:
//
cellChart = (rootView.findViewById(R.id.chart_cells) as CandleStickChart)
+ cellChart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
cellChart.apply {
description = cellChart.description.apply {
text = getString(R.string.battery_cell_description)
@@ -121,7 +151,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
setDrawBorders(true)
}
var xAxis: XAxis = cellChart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return "#" + (value.toInt() + 1)
@@ -130,19 +160,20 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
xAxis.isGranularityEnabled = true
xAxis.setGranularity(1f)
var yAxis: YAxis = cellChart.axisLeft
- yAxis.textColor = COLOR_VOLT
- yAxis.gridColor = COLOR_VOLT_GRID
+ yAxis.textColor = colorVolt
+ yAxis.gridColor = colorVoltGrid
yAxis.valueFormatter = DefaultAxisValueFormatter(2)
yAxis.setGranularity(0.01f)
yAxis = cellChart.axisRight
- yAxis.textColor = COLOR_TEMP
- yAxis.gridColor = COLOR_TEMP_GRID
+ yAxis.textColor = colorTemp
+ yAxis.gridColor = colorTempGrid
yAxis.valueFormatter = DefaultAxisValueFormatter(0)
//
// Setup Pack history chart:
//
packChart = rootView.findViewById(R.id.chart_pack) as LineChart
+ packChart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
packChart.description.isEnabled = false
packChart.setDrawGridBackground(false)
packChart.setDrawBorders(true)
@@ -165,15 +196,15 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
}
})
xAxis = packChart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.valueFormatter = PackTimeValueFormatter()
xAxis.isGranularityEnabled = true
xAxis.setGranularity(1f)
yAxis = packChart.axisLeft
yAxis.spaceTop = 5f
yAxis.spaceBottom = 5f
- yAxis.textColor = COLOR_SOC_TEXT
- yAxis.gridColor = COLOR_SOC_GRID
+ yAxis.textColor = colorSocText
+ yAxis.gridColor = colorSocGrid
yAxis.valueFormatter = object : DefaultAxisValueFormatter(0) {
override fun getFormattedValue(value: Float): String {
return super.getFormattedValue(value) + "%"
@@ -182,8 +213,8 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
yAxis = packChart.axisRight
yAxis.spaceTop = 15f
yAxis.spaceBottom = 15f
- yAxis.textColor = COLOR_VOLT
- yAxis.gridColor = COLOR_VOLT_GRID
+ yAxis.textColor = colorVolt
+ yAxis.gridColor = colorVoltGrid
yAxis.valueFormatter = object : DefaultAxisValueFormatter(1) {
override fun getFormattedValue(value: Float): String {
return super.getFormattedValue(value) + "%"
@@ -446,7 +477,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
if (packStatus.isNewSection(lastStatus)) {
val limitLine = LimitLine(xPos)
limitLine.label = timeFmt.format(packStatus.timeStamp)
- limitLine.textColor = Color.WHITE
+ limitLine.textColor = colorText
limitLine.textStyle = Paint.Style.FILL
limitLine.setTextSize(6f)
limitLine.enableDashedLine(3f, 2f, 0f)
@@ -464,7 +495,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
dataSet = LineDataSet(tempValues, getString(R.string.battery_data_temp))
packTempSet = dataSet
dataSet.axisDependency = YAxis.AxisDependency.RIGHT
- dataSet.setColor(COLOR_TEMP)
+ dataSet.setColor(colorTemp)
dataSet.setLineWidth(3f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -482,7 +513,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
dataSet = LineDataSet(voltValues, getString(R.string.battery_data_volt))
packVoltSet = dataSet
dataSet.axisDependency = YAxis.AxisDependency.RIGHT
- dataSet.setColor(COLOR_VOLT)
+ dataSet.setColor(colorVolt)
dataSet.setLineWidth(3f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -490,7 +521,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
}
dataSet = LineDataSet(socValues, getString(R.string.battery_data_soc))
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.setColor(COLOR_SOC_LINE)
+ dataSet.setColor(colorSocLine)
dataSet.setLineWidth(4f)
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
@@ -499,7 +530,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
// display data sets:
val data: LineData = LineData(dataSets)
packData = data
- data.setValueTextColor(Color.WHITE)
+ data.setValueTextColor(colorText)
data.setValueTextSize(9f)
packChart.setData(data)
val xAxis = packChart.xAxis
@@ -513,7 +544,7 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
}
xAxis.addLimitLine(l)
}
- packChart.legend.textColor = Color.WHITE
+ packChart.legend.textColor = colorText
packChart.invalidate()
}
@@ -626,10 +657,10 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
if (showTemp) {
dataSet = CandleDataSet(tempValues, getString(R.string.battery_data_temp))
dataSet.axisDependency = YAxis.AxisDependency.RIGHT
- dataSet.neutralColor = COLOR_TEMP
- dataSet.increasingColor = COLOR_TEMP
- dataSet.decreasingColor = COLOR_TEMP
- dataSet.shadowColor = COLOR_TEMP
+ dataSet.neutralColor = colorTemp
+ dataSet.increasingColor = colorTemp
+ dataSet.decreasingColor = colorTemp
+ dataSet.shadowColor = colorTemp
dataSet.setDrawValues(true)
dataSet.shadowWidth = 4f
dataSet.valueFormatter = object : ValueFormatter() {
@@ -642,10 +673,10 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
if (showVolt) {
dataSet = CandleDataSet(voltValues, getString(R.string.battery_data_volt))
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.neutralColor = COLOR_VOLT
- dataSet.increasingColor = COLOR_VOLT
- dataSet.decreasingColor = COLOR_VOLT
- dataSet.shadowColor = COLOR_VOLT
+ dataSet.neutralColor = colorVolt
+ dataSet.increasingColor = colorVolt
+ dataSet.decreasingColor = colorVolt
+ dataSet.shadowColor = colorVolt
dataSet.setDrawValues(true)
dataSet.shadowWidth = 4f
dataSet.valueFormatter = object : ValueFormatter() {
@@ -688,10 +719,10 @@ class BatteryFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCa
// display data sets:
val data = CandleData(dataSets)
- data.setValueTextColor(Color.WHITE)
+ data.setValueTextColor(colorText)
data.setValueTextSize(9f)
cellChart.setData(data)
- cellChart.legend.textColor = Color.WHITE
+ cellChart.legend.textColor = colorText
cellChart.invalidate()
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/CarFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/CarFragment.kt
index 9a089d32..61dc696b 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/CarFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/CarFragment.kt
@@ -78,7 +78,12 @@ class CarFragment : BaseFragment(), View.OnClickListener, OnResultCommandListene
// set the car background image:
val iv = findViewById(R.id.tabCarImageCarOutline) as ImageView
- iv.setImageResource(getDrawableIdentifier(activity, "ol_" + this.carData!!.sel_vehicle_image))
+ val customDrawable = Ui.getCarDrawable(activity, this.carData!!.sel_vehicle_image)
+ if (customDrawable != null) {
+ iv.setImageDrawable(customDrawable)
+ } else {
+ iv.setImageResource(getDrawableIdentifier(activity, "ol_" + this.carData!!.sel_vehicle_image))
+ }
val tabCarImageHomeLink: ImageView = findViewById(R.id.tabCarImageHomelink) as ImageView
when (carData.car_type) {
"RT" -> {
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/InfoFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/InfoFragment.kt
index dac05608..d93a7446 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/InfoFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/InfoFragment.kt
@@ -933,12 +933,17 @@ class InfoFragment : BaseFragment(), View.OnClickListener, OnResultCommandListen
)
iv.setScaleType(ImageView.ScaleType.FIT_START)
iv.setAdjustViewBounds(true)
- iv.setImageResource(
- getDrawableIdentifier(
- parent.context,
- carData.sel_vehicle_image
+ val customDrawable = Ui.getCarDrawable(parent.context, carData.sel_vehicle_image)
+ if (customDrawable != null) {
+ iv.setImageDrawable(customDrawable)
+ } else {
+ iv.setImageResource(
+ getDrawableIdentifier(
+ parent.context,
+ carData.sel_vehicle_image
+ )
)
- )
+ }
iv
} catch (e: Exception) {
null
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/MainActivity.kt b/app/src/main/java/com/openvehicles/OVMS/ui/MainActivity.kt
index 235da89b..aafe1a78 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/MainActivity.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/MainActivity.kt
@@ -42,6 +42,7 @@ import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.ui.BaseFragmentActivity.Companion.finishCurrent
import com.openvehicles.OVMS.ui.MapFragment.UpdateLocation
import com.openvehicles.OVMS.ui.utils.Database
+import com.openvehicles.OVMS.ui2.MainActivityUI2
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.ConnectionList
import com.openvehicles.OVMS.utils.ConnectionList.ConnectionsListener
@@ -164,6 +165,15 @@ class MainActivity : ApiActivity(), ActionBar.OnNavigationListener, GetMapDetail
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate")
appPrefs = AppPrefs(this, "ovms")
+
+ val isOldUI = appPrefs.getData("option_oldui_enabled", "0") == "1"
+ if (!isOldUI) {
+ Log.i(TAG, "onCreate: redirection to New UI (MainActivityUI2)")
+ startActivity(Intent(this, MainActivityUI2::class.java))
+ finish()
+ return
+ }
+
database = Database(this)
// get/create App UUID:
@@ -294,9 +304,19 @@ class MainActivity : ApiActivity(), ActionBar.OnNavigationListener, GetMapDetail
override fun onDestroy() {
Log.d(TAG, "onDestroy")
- unregisterReceiver(apiEventReceiver)
- unregisterReceiver(notificationReceiver)
- database.close()
+ try {
+ unregisterReceiver(apiEventReceiver)
+ } catch (e: Exception) {
+ // ignore
+ }
+ try {
+ unregisterReceiver(notificationReceiver)
+ } catch (e: Exception) {
+ // ignore
+ }
+ if (::database.isInitialized) {
+ database.close()
+ }
// Stop background ApiService?
val serviceEnabled = appPrefs.getData("option_service_enabled", "0") == "1"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/MapFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/MapFragment.kt
index 4d875c9c..7ec88a14 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/MapFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/MapFragment.kt
@@ -40,6 +40,8 @@ import com.openvehicles.OVMS.entities.CarData
import com.openvehicles.OVMS.ui.BaseFragmentActivity.Companion.show
import com.openvehicles.OVMS.ui.MapSettingsFragment.UpdateMap
import com.openvehicles.OVMS.ui.utils.Database
+import com.openvehicles.OVMS.ui.utils.Ui
+import android.graphics.Bitmap
import com.openvehicles.OVMS.ui.utils.DemoClusterOptionsProvider
import com.openvehicles.OVMS.ui.utils.MarkerGenerator.addMarkers
import com.openvehicles.OVMS.ui.utils.Ui.getDrawableIdentifier
@@ -112,6 +114,12 @@ class MapFragment : BaseFragment(), GoogleMap.OnInfoWindowClickListener, GetMapD
map = googleMap
Log.i(TAG, "getMap/onMapReady: map=$map")
+
+ // Set map type from prefs
+ val mapTypeStr = appPrefs.getData("map_type", GoogleMap.MAP_TYPE_NORMAL.toString())
+ val mapType = mapTypeStr?.toIntOrNull() ?: GoogleMap.MAP_TYPE_NORMAL
+ map!!.mapType = mapType
+
var clusterEnabled = true
var clusterSizeIndex = 0
try {
@@ -204,7 +212,7 @@ class MapFragment : BaseFragment(), GoogleMap.OnInfoWindowClickListener, GetMapD
// fetch chargepoints for view:
val cameraPosition = map!!.cameraPosition
Log.i(TAG, "getMap/onCameraIdle: get charge points for " + cameraPosition.target)
- //mainActivity.startGetMapDetails(cameraPosition.target)
+ (activity as? MainActivityUI2)?.startGetMapDetails(cameraPosition.target)
})
update()
}
@@ -251,6 +259,16 @@ class MapFragment : BaseFragment(), GoogleMap.OnInfoWindowClickListener, GetMapD
optionsMenu.findItem(R.id.mi_map_filter_range)
.setVisible(false)
}
+
+ // Set checked map type
+ val mapTypeStr = appPrefs.getData("map_type", GoogleMap.MAP_TYPE_NORMAL.toString())
+ val mapType = mapTypeStr?.toIntOrNull() ?: GoogleMap.MAP_TYPE_NORMAL
+ when (mapType) {
+ GoogleMap.MAP_TYPE_NORMAL -> optionsMenu.findItem(R.id.mi_map_type_normal)?.setChecked(true)
+ GoogleMap.MAP_TYPE_SATELLITE -> optionsMenu.findItem(R.id.mi_map_type_satellite)?.setChecked(true)
+ GoogleMap.MAP_TYPE_HYBRID -> optionsMenu.findItem(R.id.mi_map_type_hybrid)?.setChecked(true)
+ GoogleMap.MAP_TYPE_TERRAIN -> optionsMenu.findItem(R.id.mi_map_type_terrain)?.setChecked(true)
+ }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -275,6 +293,29 @@ class MapFragment : BaseFragment(), GoogleMap.OnInfoWindowClickListener, GetMapD
appPrefs.saveData("inrange", if (newState) "on" else "off")
item.setChecked(newState)
updateMapDetails(false)
+ } else if (menuId == R.id.mi_map_type_normal || menuId == R.id.mi_map_type_satellite ||
+ menuId == R.id.mi_map_type_hybrid || menuId == R.id.mi_map_type_terrain
+ ) {
+ val type = when (menuId) {
+ R.id.mi_map_type_satellite -> GoogleMap.MAP_TYPE_SATELLITE
+ R.id.mi_map_type_hybrid -> GoogleMap.MAP_TYPE_HYBRID
+ R.id.mi_map_type_terrain -> GoogleMap.MAP_TYPE_TERRAIN
+ else -> GoogleMap.MAP_TYPE_NORMAL
+ }
+ map?.mapType = type
+ appPrefs.saveData("map_type", type.toString())
+ item.setChecked(true)
+ } else if (menuId == R.id.mi_map_search) {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("geo:${carPosition.latitude},${carPosition.longitude}?q=charging+station")
+ )
+ intent.setPackage("com.google.android.apps.maps")
+ if (intent.resolveActivity(requireContext().packageManager) != null) {
+ startActivity(intent)
+ } else {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=charging+station")))
+ }
} else if (menuId == R.id.mi_map_settings) {
var baseActivity: MainActivity? = null
try {
@@ -469,23 +510,52 @@ class MapFragment : BaseFragment(), GoogleMap.OnInfoWindowClickListener, GetMapD
// update car position marker:
// determine icon for this car:
- val icon: Int = if (carData!!.sel_vehicle_image.startsWith("car_imiev_")) R.drawable.map_car_imiev // one map icon for all colors
- else if (carData!!.sel_vehicle_image.startsWith("car_i3_")) R.drawable.map_car_i3 // one map icon for all colors
- else if (carData!!.sel_vehicle_image.startsWith("car_smart_")) R.drawable.map_car_smart // one map icon for all colors
- else if (carData!!.sel_vehicle_image.startsWith("car_kianiro_")) R.drawable.map_car_kianiro_grey // one map icon for all colors
- else if (carData!!.sel_vehicle_image.startsWith("car_kangoo_")) R.drawable.map_car_kangoo // one map icon for all colors
- else if (carData!!.sel_vehicle_image.startsWith("car_nrjk")) R.drawable.map_car_nrjk
- else if (carData!!.sel_vehicle_image.startsWith("car_niu_mqi_gt_")) R.drawable.map_car_nrjk
- else getDrawableIdentifier(activity, "map_" + carData!!.sel_vehicle_image)
- val drawable = ResourcesCompat.getDrawable(
- resources,
- if (icon != 0) icon else R.drawable.map_car_default, null
- )
- val myLogo = (drawable as BitmapDrawable?)!!.bitmap
+ var myLogo: Bitmap? = null
+ if (carData!!.sel_vehicle_image_map.startsWith("file://")) {
+ val customDrawable = Ui.getCarDrawable(requireContext(), carData!!.sel_vehicle_image_map)
+ if (customDrawable is BitmapDrawable) {
+ myLogo = customDrawable.bitmap
+ // Scale down if too large?
+ if (myLogo.width > 116 || myLogo.height > 116) {
+ myLogo = Bitmap.createScaledBitmap(myLogo, 116, 116, true)
+ }
+ }
+ }
+
+ if (myLogo == null) {
+ val icon: Int =
+ if (carData!!.sel_vehicle_image_map.isNotEmpty() && !carData!!.sel_vehicle_image_map.startsWith("file://")) {
+ getDrawableIdentifier(activity, carData!!.sel_vehicle_image_map)
+ } else if (carData!!.sel_vehicle_image.startsWith("car_imiev_")) {
+ R.drawable.map_car_imiev // one map icon for all colors
+ } else if (carData!!.sel_vehicle_image.startsWith("car_i3_")) {
+ R.drawable.map_car_i3 // one map icon for all colors
+ } else if (carData!!.sel_vehicle_image.startsWith("car_smart_")) {
+ R.drawable.map_car_smart // one map icon for all colors
+ } else if (carData!!.sel_vehicle_image.startsWith("car_kianiro_")) {
+ R.drawable.map_car_kianiro_grey // one map icon for all colors
+ } else if (carData!!.sel_vehicle_image.startsWith("car_kangoo_")) {
+ R.drawable.map_car_kangoo // one map icon for all colors
+ } else if (carData!!.sel_vehicle_image.startsWith("car_nrjk")) {
+ R.drawable.map_car_nrjk
+ } else if (carData!!.sel_vehicle_image.startsWith("car_niu_mqi_gt_")) {
+ R.drawable.map_car_nrjk
+ } else {
+ getDrawableIdentifier(activity, "map_" + carData!!.sel_vehicle_image)
+ }
+
+ val drawable = ResourcesCompat.getDrawable(
+ resources,
+ if (icon != 0) icon else R.drawable.map_car_default, null
+ )
+ myLogo = (drawable as BitmapDrawable?)!!.bitmap
+ myLogo = Bitmap.createScaledBitmap(myLogo, 116, 116, true)
+ }
+
val marker = MarkerOptions().position(carPosition)
.title(carData!!.sel_vehicle_label)
.rotation(carData!!.car_direction.toFloat())
- .icon(BitmapDescriptorFactory.fromBitmap(myLogo))
+ .icon(BitmapDescriptorFactory.fromBitmap(myLogo!!))
val carMarker = map!!.addMarker(marker)
carMarker.clusterGroup = ClusterGroup.NOT_CLUSTERED
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/PowerFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/PowerFragment.kt
index b2589824..b5474c13 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/PowerFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/PowerFragment.kt
@@ -1,5 +1,6 @@
package com.openvehicles.OVMS.ui
+import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
@@ -16,6 +17,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
+import androidx.core.graphics.ColorUtils
import com.github.mikephil.charting.charts.BarLineChartBase
import com.github.mikephil.charting.charts.Chart
import com.github.mikephil.charting.charts.LineChart
@@ -29,6 +31,7 @@ import com.github.mikephil.charting.formatter.ValueFormatter
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture
import com.github.mikephil.charting.listener.OnChartGestureListener
+import com.google.android.material.color.MaterialColors
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.entities.CarData
import com.openvehicles.OVMS.entities.CmdSeries
@@ -60,6 +63,15 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
private var carData: CarData? = null
private var cmdSeries: CmdSeries? = null
private lateinit var appPrefs: AppPrefs
+
+ private var colorText = Color.WHITE
+ private var colorGrid = Color.GRAY
+ private var colorPowerAvg = COLOR_POWER_AVG
+ private var colorEnergyAvg = COLOR_ENERGY_AVG
+ private var colorAltitude = COLOR_ALTITUDE
+ private var colorAltitudeGrid = COLOR_ALTITUDE_GRID
+ private var colorSpeed = COLOR_SPEED
+ private var colorSpeedGrid = COLOR_SPEED_GRID
private val isPackValid: Boolean
// Check data model validity
@@ -81,6 +93,21 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
showPower = true
}
+ // Init theme colors:
+ val ctx = requireContext()
+ colorText = MaterialColors.getColor(ctx, android.R.attr.textColorPrimary, Color.WHITE)
+ colorGrid = ColorUtils.setAlphaComponent(colorText, 40)
+
+ val isNight = (ctx.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ if (!isNight) {
+ colorPowerAvg = Color.RED
+ colorEnergyAvg = Color.parseColor("#999900")
+ colorAltitude = Color.parseColor("#998833")
+ colorAltitudeGrid = ColorUtils.setAlphaComponent(colorAltitude, 100)
+ colorSpeed = Color.parseColor("#333399")
+ colorSpeedGrid = ColorUtils.setAlphaComponent(colorSpeed, 100)
+ }
+
// Setup UI:
//val progressOverlay = createProgressOverlay(inflater, container, false)
//progressOverlay.setOnCancelListener(this)
@@ -93,23 +120,24 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
// Setup trip chart:
//
var chart: LineChart = rootView.findViewById(R.id.chart_trip) as LineChart
+ chart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
tripChart = chart
chart.description.isEnabled = false
chart.setDrawGridBackground(false)
chart.setDrawBorders(true)
chart.isHighlightPerTapEnabled = false
xAxis = chart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.valueFormatter = xFormatter
xAxis.granularity = 1f
xAxis.isGranularityEnabled = true
yAxis = chart.axisLeft // altitude
- yAxis.textColor = COLOR_ALTITUDE
- yAxis.gridColor = COLOR_ALTITUDE_GRID
+ yAxis.textColor = colorAltitude
+ yAxis.gridColor = colorAltitudeGrid
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
yAxis = chart.axisRight // speed
- yAxis.textColor = COLOR_SPEED
- yAxis.gridColor = COLOR_SPEED_GRID
+ yAxis.textColor = colorSpeed
+ yAxis.gridColor = colorSpeedGrid
yAxis.axisMinimum = 0f
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
@@ -117,47 +145,49 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
// Setup power chart:
//
chart = rootView.findViewById(R.id.chart_power) as LineChart
+ chart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
powerChart = chart
chart.description.isEnabled = false
chart.setDrawGridBackground(false)
chart.setDrawBorders(true)
chart.isHighlightPerTapEnabled = false
xAxis = chart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.valueFormatter = xFormatter
xAxis.granularity = 1f
xAxis.isGranularityEnabled = true
yAxis = chart.axisLeft
- yAxis.textColor = Color.WHITE
- yAxis.gridColor = Color.LTGRAY
+ yAxis.textColor = colorText
+ yAxis.gridColor = colorGrid
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
yAxis.setDrawTopYLabelEntry(false)
yAxis = chart.axisRight
- yAxis.textColor = Color.WHITE
- yAxis.gridColor = Color.LTGRAY
+ yAxis.textColor = colorText
+ yAxis.gridColor = colorGrid
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
//
// Setup energy chart:
//
chart = rootView.findViewById(R.id.chart_energy) as LineChart
+ chart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
energyChart = chart
chart.description.isEnabled = false
chart.setDrawGridBackground(false)
chart.setDrawBorders(true)
chart.isHighlightPerTapEnabled = false
xAxis = chart.xAxis
- xAxis.textColor = Color.WHITE
+ xAxis.textColor = colorText
xAxis.valueFormatter = xFormatter
xAxis.granularity = 1f
xAxis.isGranularityEnabled = true
yAxis = chart.axisLeft
- yAxis.textColor = Color.WHITE
- yAxis.gridColor = Color.LTGRAY
+ yAxis.textColor = colorText
+ yAxis.gridColor = colorGrid
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
yAxis = chart.axisRight
- yAxis.textColor = Color.WHITE
- yAxis.gridColor = Color.LTGRAY
+ yAxis.textColor = colorText
+ yAxis.gridColor = colorGrid
yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
//
@@ -451,7 +481,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
if (entry.isSectionStart(refEntry)) {
val l = LimitLine(xpos)
l.label = String.format("%.0f", entry.getOdometer(units))
- l.textColor = Color.WHITE
+ l.textColor = colorText
l.textStyle = Paint.Style.FILL
l.textSize = 6f
l.enableDashedLine(3f, 2f, 0f)
@@ -497,7 +527,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
// display data sets:
var data: LineData
data = LineData(dataSets)
- data.setValueTextColor(Color.WHITE)
+ data.setValueTextColor(colorText)
data.setValueTextSize(9f)
tripChart.data = data
xAxis = tripChart.xAxis
@@ -511,7 +541,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
}
xAxis.addLimitLine(l)
}
- tripChart.legend.textColor = Color.WHITE
+ tripChart.legend.textColor = colorText
tripChart.invalidate()
//
@@ -543,7 +573,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
dataSets.add(dataSet)
dataSet = LineDataSet(pwrAvgValues, getString(R.string.power_data_pwr_avg))
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.color = COLOR_POWER_AVG
+ dataSet.color = colorPowerAvg
dataSet.setDrawFilled(false)
dataSet.lineWidth = 4f
dataSet.setDrawCircles(false)
@@ -626,7 +656,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
getString(R.string.power_data_energy_avg, carData!!.car_distance_units)
)
dataSet.axisDependency = YAxis.AxisDependency.LEFT
- dataSet.color = COLOR_ENERGY_AVG
+ dataSet.color = colorEnergyAvg
dataSet.setDrawFilled(false)
dataSet.lineWidth = 4f
dataSet.setDrawCircles(false)
@@ -637,7 +667,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
// display data sets:
data = LineData(dataSets)
- data.setValueTextColor(Color.WHITE)
+ data.setValueTextColor(colorText)
data.setValueTextSize(9f)
energyChart.data = data
xAxis = energyChart.xAxis
@@ -651,7 +681,7 @@ class PowerFragment : BaseFragment(), CmdSeries.Listener, ProgressOverlay.OnCanc
}
xAxis.addLimitLine(l)
}
- energyChart.legend.textColor = Color.WHITE
+ energyChart.legend.textColor = colorText
energyChart.invalidate()
}
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/settings/GlobalOptionsFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/settings/GlobalOptionsFragment.kt
index c82ec4d3..9207208a 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/settings/GlobalOptionsFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/settings/GlobalOptionsFragment.kt
@@ -20,6 +20,7 @@ import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.ui.BaseFragment
import com.openvehicles.OVMS.ui.utils.Ui.setOnClick
import com.openvehicles.OVMS.ui.utils.Ui.setValue
+import com.openvehicles.OVMS.ui2.MainActivityUI2
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.Sys.getRandomString
@@ -88,6 +89,12 @@ class GlobalOptionsFragment : BaseFragment(), View.OnClickListener, OnFocusChang
R.id.cb_options_oldui -> {
oldUiEnabled = (v as CheckBox).isChecked
appPrefs!!.saveData("option_oldui_enabled", if (oldUiEnabled) "1" else "0")
+ if (!oldUiEnabled) {
+ // Switch to new UI immediately:
+ val intent = Intent(context, MainActivityUI2::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ startActivity(intent)
+ }
}
R.id.cb_options_service -> {
serviceEnabled = (v as CheckBox).isChecked
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/settings/StoredCommandFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui/settings/StoredCommandFragment.kt
index 4f08655f..eb72e6a5 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui/settings/StoredCommandFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/settings/StoredCommandFragment.kt
@@ -2,7 +2,6 @@ package com.openvehicles.OVMS.ui.settings
import android.app.Activity
import android.content.Context
-import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -18,7 +17,7 @@ import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.Toast
-import androidx.appcompat.app.AlertDialog
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.content.res.ResourcesCompat
@@ -55,9 +54,6 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- if (container == null) {
- return null
- }
val context = context
?: return null
@@ -75,12 +71,13 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
// Create user interface:
activity?.setTitle(R.string.stored_commands_title)
setHasOptionsMenu(true)
- listView = ListView(container.context)
- listView.fitsSystemWindows = true
+
+ val rootView = inflater.inflate(R.layout.fragment_stored_commands, container, false)
+ listView = rootView.findViewById(R.id.listView)
listView.onItemClickListener = this
adapter = StoredCommandAdapter(context, R.layout.item_stored_command, storedCommands, inflater)
listView.setAdapter(adapter)
- return listView
+ return rootView
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -118,7 +115,7 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
private fun confirmDeleteCommand(cmd: StoredCommand) {
val context = context ?: return
- AlertDialog.Builder(context)
+ MaterialAlertDialogBuilder(context)
.setTitle(R.string.Delete)
.setMessage(getString(R.string.confirm_delete_stored_command, cmd.title))
.setNegativeButton(R.string.Cancel, null)
@@ -132,14 +129,14 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
val context = context
?: return
- val view = LayoutInflater.from(context).inflate(R.layout.dlg_stored_command, null)
+ val view = LayoutInflater.from(context).inflate(R.layout.dlg_stored_command, null, false)
if (cmd == null) {
// Create new command:
- val dialog = AlertDialog.Builder(context)
+ val dialog = MaterialAlertDialogBuilder(context)
.setMessage(R.string.stored_commands_add)
.setView(view)
.setNegativeButton(R.string.Cancel, null)
- .setPositiveButton(R.string.Save) { dialog1: DialogInterface?, which: Int ->
+ .setPositiveButton(R.string.Save) { _, _ ->
val cmd1 = StoredCommand(
getValue(view, R.id.etxt_input_title),
getValue(view, R.id.etxt_input_command)
@@ -153,15 +150,15 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
view.tag = cmd
setValue(view, R.id.etxt_input_title, cmd.title)
setValue(view, R.id.etxt_input_command, cmd.command)
- val dialog = AlertDialog.Builder(context)
+ val dialog = MaterialAlertDialogBuilder(context)
.setMessage(R.string.stored_commands_edit)
.setView(view)
.setNegativeButton(R.string.Cancel, null)
- .setNeutralButton(R.string.Delete) { dialog12: DialogInterface?, which: Int ->
+ .setNeutralButton(R.string.Delete) { _, _ ->
val cmd12 = view.tag as StoredCommand
deleteCommand(cmd12)
}
- .setPositiveButton(R.string.Save) { dialog13: DialogInterface?, which: Int ->
+ .setPositiveButton(R.string.Save) { _, _ ->
val cmd13 = view.tag as StoredCommand
cmd13.title = getValue(view, R.id.etxt_input_title)
cmd13.command = getValue(view, R.id.etxt_input_command)
@@ -266,7 +263,7 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view = convertView
if (view == null) {
- view = inflater.inflate(R.layout.item_stored_command, null)
+ view = inflater.inflate(R.layout.item_stored_command, parent, false)
}
setOnClick(view!!, R.id.linearLayout1, position, this, null)
setOnClick(view, R.id.btn_select, position, this, null)
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui/utils/Ui.kt b/app/src/main/java/com/openvehicles/OVMS/ui/utils/Ui.kt
index 288da0b4..9f10d2fe 100755
--- a/app/src/main/java/com/openvehicles/OVMS/ui/utils/Ui.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui/utils/Ui.kt
@@ -21,6 +21,11 @@ import com.google.android.material.textfield.TextInputEditText
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.ui.validators.ValidationException
import com.openvehicles.OVMS.ui.validators.Validator
+import androidx.core.content.ContextCompat
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import java.io.File
object Ui {
@@ -146,6 +151,29 @@ object Ui {
}
}
+ @JvmStatic
+ fun getCarDrawable(context: Context?, name: String?): Drawable? {
+ if (name == null || context == null) return null
+
+ if (name.startsWith("file://")) {
+ val path = name.substring(7)
+ val file = File(path)
+ if (file.exists()) {
+ val bitmap = BitmapFactory.decodeFile(path)
+ if (bitmap != null) {
+ return BitmapDrawable(context.resources, bitmap)
+ }
+ }
+ }
+
+ val resId = getDrawableIdentifier(context, name)
+ return if (resId > 0) {
+ ContextCompat.getDrawable(context, resId)
+ } else {
+ null
+ }
+ }
+
@JvmStatic
fun getStringIdentifier(context: Context?, name: String?): Int {
return if (name == null || context == null) {
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/MainActivityUI2.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/MainActivityUI2.kt
index 228349bb..d9131922 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/MainActivityUI2.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/MainActivityUI2.kt
@@ -7,6 +7,8 @@ import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
@@ -20,7 +22,12 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.NavController
+import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI
@@ -28,6 +35,7 @@ import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.material.appbar.MaterialToolbar
+import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
@@ -36,12 +44,18 @@ import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.ui.ApiActivity
import com.openvehicles.OVMS.ui.MainActivity
+import com.google.android.gms.maps.model.LatLng
+import com.openvehicles.OVMS.ui.GetMapDetails
+import com.openvehicles.OVMS.ui.GetMapDetailsListener
import com.openvehicles.OVMS.ui.MapFragment
+import com.openvehicles.OVMS.ui.utils.Database
+import com.openvehicles.OVMS.ui2.misc.ThemeMode
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.Sys.getRandomString
import java.util.UUID
+import android.os.AsyncTask
-class MainActivityUI2 : ApiActivity() {
+class MainActivityUI2 : ApiActivity(), GetMapDetailsListener {
companion object {
var updateLocation: MapFragment.UpdateLocation? = null
@@ -50,6 +64,7 @@ class MainActivityUI2 : ApiActivity() {
}
private lateinit var navController: NavController
+
/**
* ApiService / OVMS server communication:
*
@@ -57,6 +72,7 @@ class MainActivityUI2 : ApiActivity() {
private var apiErrorDialog: AlertDialog? = null
private var apiErrorMessage: String? = null
private lateinit var appPrefs: AppPrefs
+ private lateinit var database: Database
private lateinit var uuid: String
private var versionName = ""
private var versionCode = 0
@@ -64,6 +80,11 @@ class MainActivityUI2 : ApiActivity() {
private var tokenRequested = false
private var apiEventReceiver: BroadcastReceiver? = null
+ private var getMapDetails: GetMapDetails? = null
+ private val getMapDetailList: MutableList = ArrayList()
+ private var getMapDetailsBlockUntil: Long = 0
+ private var getMapDetailsBlockSeconds: Long = 0
+
private val gcmHandler = Handler(Looper.getMainLooper())
private val gcmRegistrationBroadcastReceiver: BroadcastReceiver =
object : BroadcastReceiver() {
@@ -78,12 +99,48 @@ class MainActivityUI2 : ApiActivity() {
}
private val listener = NavController.OnDestinationChangedListener { controller, destination, arguments ->
- findViewById(R.id.spinner_toolbar).visibility = if (destination.id == R.id.navigation_home) View.VISIBLE else View.GONE
+ val onHome = destination.id == R.id.navigation_home
+ val spinner = findViewById(R.id.spinner_toolbar)
+ spinner?.visibility = if (onHome) View.VISIBLE else View.GONE
+
+ supportActionBar?.setDisplayShowTitleEnabled(!onHome)
+
+ updateStatusBarIcons(onHome)
+ }
+
+ /**
+ * Applies edge-to-edge rendering.
+ */
+ private fun applyEdgeToEdge() {
+ WindowCompat.setDecorFitsSystemWindows(window, true)
+ }
+
+ /** True when the dark theme is currently in effect. */
+ private fun isDarkTheme(): Boolean {
+ val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ return mode == Configuration.UI_MODE_NIGHT_YES
+ }
+
+ /**
+ * Updates the status bar icon colour.
+ */
+ private fun updateStatusBarIcons(onHome: Boolean) {
+ val isDark = isDarkTheme()
+ val controller = WindowInsetsControllerCompat(window, window.decorView)
+ // Dark icons (isAppearanceLightStatusBars = true) on light background.
+ controller.isAppearanceLightStatusBars = !isDark
}
+
override fun onCreate(savedInstanceState: Bundle?) {
+ ThemeMode.apply(this)
super.onCreate(savedInstanceState)
appPrefs = AppPrefs(this, "ovms")
- if (appPrefs.getData("option_oldui_enabled", "0") == "1") {
+ database = Database(this)
+
+ val isOldUI = appPrefs.getData("option_oldui_enabled", "0") == "1"
+ Log.d(TAG, "onCreate: option_oldui_enabled = $isOldUI")
+ if (isOldUI) {
+ Log.i(TAG, "onCreate: starting Old UI (MainActivity)")
startActivity(Intent(this, MainActivity::class.java))
finish()
return
@@ -159,6 +216,8 @@ class MainActivityUI2 : ApiActivity() {
val toolbar: MaterialToolbar = findViewById(R.id.materialToolbar);
setSupportActionBar(toolbar)
+ applyEdgeToEdge()
+
// Start background ApiService:
Log.i(TAG, "onCreate: starting ApiService")
try {
@@ -178,7 +237,7 @@ class MainActivityUI2 : ApiActivity() {
checkVersion()
val abc = AppBarConfiguration.Builder(R.id.navigation_home).build();
- val navHostFragment = supportFragmentManager.fragments.first() as NavHostFragment
+ val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main2) as NavHostFragment
navController = navHostFragment.navController
NavigationUI.setupActionBarWithNavController(this, navHostFragment.navController, abc)
@@ -472,6 +531,82 @@ class MainActivityUI2 : ApiActivity() {
return true
}
+ private fun isMapCacheValid(center: LatLng): Boolean {
+ val latitude = center.latitude.toInt()
+ val longitude = center.longitude.toInt()
+ val cursor = database.getLatLngDetail(latitude, longitude)
+ val colLastUpdate = cursor.getColumnIndex("last_update")
+ if (cursor.count == 0) {
+ cursor.close()
+ return false
+ } else if (cursor.moveToFirst()) {
+ val lastUpdate = if (colLastUpdate >= 0) cursor.getLong(colLastUpdate) else 0
+ val now = System.currentTimeMillis() / 1000
+ if (now > lastUpdate + 3600 * 24) {
+ cursor.close()
+ return false
+ }
+ }
+ Log.v(TAG, "isMapCacheValid: cache valid for lat/lng=$latitude/$longitude")
+ cursor.close()
+ return true
+ }
+
+ fun startGetMapDetails(center: LatLng) {
+ if (!isMapCacheValid(center)) {
+ getMapDetailList.add(center)
+ startGetMapDetails()
+ } else {
+ Log.v(TAG, "StartGetMapDetails: map cache valid for center=$center")
+ }
+ }
+
+ private fun startGetMapDetails() {
+ if (getMapDetails != null && getMapDetails!!.status != AsyncTask.Status.FINISHED) {
+ return
+ }
+ if (System.currentTimeMillis() < getMapDetailsBlockUntil) {
+ return
+ }
+ if (appPrefs.getData("option_ocm_enabled", "1") == "0") {
+ return
+ }
+ do {
+ if (getMapDetailList.isEmpty()) {
+ Log.i(TAG, "StartGetMapDetails: done.")
+ return
+ }
+ val center = getMapDetailList.removeAt(0)
+ if (center == null || isMapCacheValid(center)) {
+ continue
+ }
+
+ Log.i(TAG, "StartGetMapDetails: starting task for $center")
+ getMapDetails = GetMapDetails(this@MainActivityUI2, center, this)
+ getMapDetails!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
+ return
+ } while (true)
+ }
+
+ override fun getMapDetailsDone(isSuccess: Boolean, center: LatLng?) {
+ if (isSuccess) {
+ Log.i(TAG, "getMapDetailsDone: OCM updates received for $center")
+ database.addLatLngDetail(center!!.latitude.toInt(), center.longitude.toInt())
+
+ MapFragment.updateMap.updateFilter(null) // trigger update
+
+ getMapDetailsBlockUntil = 0
+ getMapDetailsBlockSeconds = 0
+ startGetMapDetails()
+ } else {
+ Log.e(TAG, "getMapDetailsDone: OCM updates failed for $center")
+ getMapDetailsBlockSeconds = if (getMapDetailsBlockSeconds == 0L) 15L else getMapDetailsBlockSeconds * 2
+ if (getMapDetailsBlockSeconds > 120L) getMapDetailsBlockSeconds = 120L
+ getMapDetailsBlockUntil = System.currentTimeMillis() + getMapDetailsBlockSeconds * 1000
+ startGetMapDetails()
+ }
+ }
+
override fun onResume() {
super.onResume()
@@ -492,8 +627,14 @@ class MainActivityUI2 : ApiActivity() {
override fun onDestroy() {
Log.d(TAG, "onDestroy")
- if (apiEventReceiver != null)
- unregisterReceiver(apiEventReceiver)
+ database.close()
+
+ try {
+ if (apiEventReceiver != null)
+ unregisterReceiver(apiEventReceiver)
+ } catch (e: Exception) {
+ // ignore
+ }
// Stop background ApiService?
val serviceEnabled = appPrefs.getData("option_service_enabled", "0") == "1"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/energymetrics/EnergyMetricsAdapter.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/energymetrics/EnergyMetricsAdapter.kt
index 7378e209..136a40c6 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/energymetrics/EnergyMetricsAdapter.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/energymetrics/EnergyMetricsAdapter.kt
@@ -1,14 +1,15 @@
package com.openvehicles.OVMS.ui2.components.energymetrics
import android.content.Context
-import android.os.Build
+import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
+import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.color.MaterialColors
import com.openvehicles.OVMS.R
-import androidx.core.graphics.toColorInt
class EnergyMetricsAdapter internal constructor(
@@ -30,17 +31,16 @@ class EnergyMetricsAdapter internal constructor(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val metric = mData[position]
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- if (position.and(1) == 0)
- holder.itemView.setBackgroundResource(R.color.material_dynamic_secondary20)
- else
- holder.itemView.setBackgroundResource(R.color.material_dynamic_secondary10)
+
+ // Zebra striping using theme colors
+ if (position % 2 == 0) {
+ holder.itemView.setBackgroundColor(Color.TRANSPARENT)
} else {
- if (position.and(1) == 0)
- holder.itemView.setBackgroundColor("#2A3042".toColorInt())
- else
- holder.itemView.setBackgroundColor("#151B2C".toColorInt())
+ val baseColor = MaterialColors.getColor(holder.itemView, R.attr.colorOnSurface, Color.BLACK)
+ val stripeColor = ColorUtils.setAlphaComponent(baseColor, 15) // very subtle overlay
+ holder.itemView.setBackgroundColor(stripeColor)
}
+
holder.metricName.text = metric.metricName
holder.metricValue.text = metric.metricValue
holder.clickListener = mClickListener
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/hometabs/HomeTabsAdapter.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/hometabs/HomeTabsAdapter.kt
index 8c45608f..4d6ad7b9 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/hometabs/HomeTabsAdapter.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/hometabs/HomeTabsAdapter.kt
@@ -45,9 +45,9 @@ class HomeTabsAdapter internal constructor(
if (defaultCardColor == null || defaultOnCardColor == null) {
val tv = TypedValue()
val theme = context.theme
- theme.resolveAttribute(com.google.android.material.R.attr.colorSecondaryContainer, tv, true)
+ theme.resolveAttribute(com.google.android.material.R.attr.colorSurfaceContainer, tv, true)
defaultCardColor = tv.data
- theme.resolveAttribute(com.google.android.material.R.attr.colorOnSecondaryContainer, tv, true)
+ theme.resolveAttribute(com.google.android.material.R.attr.colorOnSurface, tv, true)
defaultOnCardColor = tv.data
}
holder.tabName.text = action.tabName
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/CarInfoQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/CarInfoQuickAction.kt
index e0845489..7d3f2696 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/CarInfoQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/CarInfoQuickAction.kt
@@ -13,8 +13,11 @@ import com.openvehicles.OVMS.ui.settings.CarInfoFragment
class CarInfoQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_homelink, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background,
- label = context?.getString(R.string.service_notification_title)) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.service_notification_title)
+ ) {
companion object {
const val ACTION_ID = "carinfo"
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ChargingQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ChargingQuickAction.kt
index d086e88b..8c233800 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ChargingQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ChargingQuickAction.kt
@@ -11,7 +11,11 @@ import com.openvehicles.OVMS.api.ApiService
class ChargingQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_charging, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background, label = context?.getString(R.string.state_charging_label)) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.state_charging_label)
+ ) {
companion object {
const val ACTION_ID = "charging"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateQuickAction.kt
index cd49c3e6..da0cd614 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateQuickAction.kt
@@ -12,7 +12,9 @@ import com.openvehicles.OVMS.api.ApiService
class ClimateQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_ac, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background,
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
label = context?.getString(R.string.climate_control_short)) {
companion object {
const val ACTION_ID = "climate"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateScheduleQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateScheduleQuickAction.kt
index fb472328..2c0f946e 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateScheduleQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ClimateScheduleQuickAction.kt
@@ -26,7 +26,9 @@ class ClimateScheduleQuickAction(
R.drawable.heat_cool_w,
apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background,
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
label = context?.getString(R.string.lb_climate_schedule)
) {
companion object {
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/DDT4allQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/DDT4allQuickAction.kt
index 4d11f797..bb620bca 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/DDT4allQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/DDT4allQuickAction.kt
@@ -12,8 +12,11 @@ import com.openvehicles.OVMS.api.ApiService
class DDT4allQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_controls_tab, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.attr.colorTertiaryContainer,
- label = context?.getString(R.string.select_ddt4all_action)) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.select_ddt4all_action)
+ ) {
companion object {
const val ACTION_ID = "ddt4all"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/HomelinkQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/HomelinkQuickAction.kt
index 66ef04b9..39db446d 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/HomelinkQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/HomelinkQuickAction.kt
@@ -17,7 +17,12 @@ class Homelink2QuickAction(apiServiceGetter: () -> ApiService?, context: Context
class Homelink3QuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null):
HomelinkQuickAction(2, R.drawable.ic_homelink_3, "${context?.getString(R.string.Garage)} 3", apiServiceGetter)
open class HomelinkQuickAction(val garageNumber: Int, icon: Int, label: String?, apiServiceGetter: () -> ApiService?) :
- QuickAction("hl_$garageNumber", icon, apiServiceGetter, label = label) {
+ QuickAction("hl_$garageNumber", icon, apiServiceGetter,
+ actionOnTint = R.attr.colorSecondaryContainer,
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = label) {
override fun onAction() {
sendCommand("24,${garageNumber}")
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/LockQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/LockQuickAction.kt
index 4a04aac8..0b823252 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/LockQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/LockQuickAction.kt
@@ -14,8 +14,11 @@ import com.openvehicles.OVMS.ui.utils.Ui
class LockQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_lock_open, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.attr.colorTertiaryContainer,
- label = context?.getString(R.string.central_locking_action_label)) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.central_locking_action_label)
+ ) {
companion object {
const val ACTION_ID = "lock"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/QuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/QuickAction.kt
index 2c76a885..31f13e7d 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/QuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/QuickAction.kt
@@ -3,12 +3,14 @@ package com.openvehicles.OVMS.ui2.components.quickactions
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
import android.util.TypedValue
import android.view.View
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.appcompat.content.res.AppCompatResources
-import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.color.MaterialColors
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.api.ApiService
@@ -24,10 +26,12 @@ open class QuickAction {
private val icon: Int
private var actionOnTint: Int? = null
private var actionOffTint: Int? = null
+ private var actionOnIconTint: Int? = null
+ private var actionOffIconTint: Int? = null
private var carData: CarData? = null
private var actionIsRunningCommand = false
lateinit var progressBar: CircularProgressIndicator
- lateinit var button: FloatingActionButton
+ lateinit var button: MaterialButton
var context: Context? = null
private var getApiService: () -> ApiService?
@@ -37,12 +41,16 @@ open class QuickAction {
getApiService: () -> ApiService?,
actionOnTint: Int? = null,
actionOffTint: Int? = null,
- label: String? = null
+ label: String? = null,
+ actionOnIconTint: Int? = null,
+ actionOffIconTint: Int? = null
) {
this.id = id
this.icon = icon
this.actionOnTint = actionOnTint
this.actionOffTint = actionOffTint
+ this.actionOnIconTint = actionOnIconTint
+ this.actionOffIconTint = actionOffIconTint
this.getApiService = getApiService
this.label = label
}
@@ -51,7 +59,7 @@ open class QuickAction {
fun initAction(view: View, clickCallback: () -> Boolean, editMode: Boolean) {
// Init elements and let quick action do handling
context = view.context
- button = view.findViewById(R.id.action_button) as FloatingActionButton
+ button = view.findViewById(R.id.action_button) as MaterialButton
progressBar = view.findViewById(R.id.action_progress) as CircularProgressIndicator
button.setOnClickListener {
if (clickCallback()) {
@@ -72,7 +80,7 @@ open class QuickAction {
fun setCommandInProgress(value: Boolean) {
this.commandInProgress = value
- this.progressBar.visibility = if (value) View.VISIBLE else View.INVISIBLE
+ this.progressBar.visibility = if (value) View.VISIBLE else View.GONE
this.button.isEnabled = commandsAvailable() && !value
}
@@ -171,6 +179,9 @@ open class QuickAction {
open fun renderAction(editMode: Boolean) {
val commandsAvailable = commandsAvailable()
button.isEnabled = commandsAvailable || editMode
+
+ val iconDrawable = getLiveCarIcon(actionOn, context!!)
+
if (actionOnTint != null && actionOffTint != null && !editMode) {
try {
button.backgroundTintList =
@@ -184,14 +195,40 @@ open class QuickAction {
button.backgroundTintList =
ColorStateList.valueOf(color)
}
+
+ // Icon Tint Handling for custom states
+ if (actionOnIconTint != null && actionOffIconTint != null) {
+ try {
+ button.iconTint =
+ context?.resources?.getColor(if (actionOn) actionOnIconTint!! else actionOffIconTint!!)
+ ?.let { ColorStateList.valueOf(it) }
+ } catch (ignored: java.lang.Exception) {
+ val typedValue = TypedValue()
+ val theme = context!!.theme
+ theme.resolveAttribute(if (actionOn) actionOnIconTint!! else actionOffIconTint!!, typedValue, true)
+ @ColorInt val color = typedValue.data
+ button.iconTint = ColorStateList.valueOf(color)
+ }
+ } else if (iconDrawable !is LayerDrawable) {
+ val typedValue = TypedValue()
+ context!!.theme.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true)
+ button.iconTint = ColorStateList.valueOf(typedValue.data)
+ } else {
+ button.iconTint = null
+ }
} else {
- val typedValue = TypedValue()
- val theme = context!!.theme
- theme.resolveAttribute(R.attr.colorPrimarySurface, typedValue, true)
- @ColorInt val color = typedValue.data
- button.backgroundTintList =
- ColorStateList.valueOf(color)
+ // Use default Material3 Secondary Container colors for edit mode or standard state
+ val backgroundColor = MaterialColors.getColor(button, R.attr.colorSecondaryContainer)
+ button.backgroundTintList = ColorStateList.valueOf(backgroundColor)
+
+ if (iconDrawable !is LayerDrawable) {
+ val iconColor = MaterialColors.getColor(button, R.attr.colorOnSecondaryContainer)
+ button.iconTint = ColorStateList.valueOf(iconColor)
+ } else {
+ button.iconTint = null
+ }
}
- button.setImageDrawable(getLiveCarIcon(actionOn, context!!))
+
+ button.icon = iconDrawable
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ValetQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ValetQuickAction.kt
index c353cf16..715bad29 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ValetQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/ValetQuickAction.kt
@@ -12,8 +12,11 @@ import com.openvehicles.OVMS.ui.utils.Ui
class ValetQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
QuickAction(ACTION_ID, R.drawable.ic_valet, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background,
- label = context?.getString(R.string.lb_valet_mode)) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.lb_valet_mode)
+ ) {
companion object {
const val ACTION_ID = "valet"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/WakeupQuickAction.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/WakeupQuickAction.kt
index 055de44e..4e519912 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/WakeupQuickAction.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/WakeupQuickAction.kt
@@ -8,9 +8,13 @@ import com.openvehicles.OVMS.api.ApiService
* Quick action handling custom commands
*/
class WakeupQuickAction(apiServiceGetter: () -> ApiService?, context: Context? = null) :
- QuickAction(ACTION_ID, R.drawable.ic_car_sleep, apiServiceGetter, label = context?.getString(R.string.Wakeup),
+ QuickAction(ACTION_ID, R.drawable.ic_car_sleep, apiServiceGetter,
actionOnTint = R.attr.colorSecondaryContainer,
- actionOffTint = R.color.cardview_dark_background) {
+ actionOffTint = R.color.cardview_off_background,
+ actionOnIconTint = R.attr.colorOnSecondaryContainer,
+ actionOffIconTint = R.color.colorText,
+ label = context?.getString(R.string.Wakeup),
+ ) {
companion object {
const val ACTION_ID = "wakeup"
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/adapters/QuickActionsEditorAdapter.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/adapters/QuickActionsEditorAdapter.kt
index 5781e46f..b5ff39e4 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/adapters/QuickActionsEditorAdapter.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/components/quickactions/adapters/QuickActionsEditorAdapter.kt
@@ -6,7 +6,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
-import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.button.MaterialButton
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.ui2.components.quickactions.QuickAction
@@ -30,9 +30,9 @@ class QuickActionsEditorAdapter internal constructor(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val action = mData[position]
- val button = holder.itemView.findViewById(R.id.action_button) as FloatingActionButton
+ val button = holder.itemView.findViewById(R.id.action_button) as MaterialButton
val label = holder.itemView.findViewById(R.id.quick_action_label) as TextView
- button.setImageDrawable(action.getLiveCarIcon(false, holder.itemView.context))
+ button.icon = action.getLiveCarIcon(false, holder.itemView.context)
button.setOnClickListener {
actionClickListener(action.id)
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/misc/ThemeMode.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/misc/ThemeMode.kt
new file mode 100644
index 00000000..d2d0f99c
--- /dev/null
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/misc/ThemeMode.kt
@@ -0,0 +1,57 @@
+package com.openvehicles.OVMS.ui2.misc
+
+import android.content.Context
+import android.util.Log
+import androidx.appcompat.app.AppCompatDelegate
+import com.openvehicles.OVMS.utils.AppPrefs
+
+/**
+ * Light / dark / follow system.
+ *
+ * The choice is stored in the "ovms" preferences and applied through
+ * [AppCompatDelegate], so it survives restarts and applies to every Activity without
+ * each screen having to know about it.
+ */
+object ThemeMode {
+
+ const val PREF_KEY = "app_theme_mode"
+
+ const val LIGHT = "light"
+ const val DARK = "dark"
+ const val SYSTEM = "system"
+
+ /** Default: follow the system theme. */
+ const val DEFAULT = SYSTEM
+
+ fun read(context: Context): String {
+ val prefs = AppPrefs(context, "ovms")
+ val value = prefs.getData(PREF_KEY, DEFAULT)
+ return if (value.isNullOrBlank()) DEFAULT else value
+ }
+
+ fun write(context: Context, mode: String) {
+ AppPrefs(context, "ovms").saveData(PREF_KEY, mode)
+ }
+
+ private fun toDelegateMode(mode: String): Int = when (mode) {
+ LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
+ DARK -> AppCompatDelegate.MODE_NIGHT_YES
+ else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
+ }
+
+ /** Applies the stored choice. Call once on application start. */
+ fun apply(context: Context) {
+ val mode = read(context)
+ Log.d("ThemeMode", "apply: mode=$mode")
+ AppCompatDelegate.setDefaultNightMode(toDelegateMode(mode))
+ }
+
+ /**
+ * Stores and applies a new choice. Activities recreate themselves, so the change
+ * is visible immediately.
+ */
+ fun applyAndStore(context: Context, mode: String) {
+ write(context, mode)
+ AppCompatDelegate.setDefaultNightMode(toDelegateMode(mode))
+ }
+}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ChargingFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ChargingFragment.kt
index 397afbe5..f15b9474 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ChargingFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ChargingFragment.kt
@@ -201,7 +201,8 @@ class ChargingFragment : BaseFragment(), OnResultCommandListener {
val socBattLayers = emptyList().toMutableList()
val socBackground = ContextCompat.getDrawable(requireContext(), R.drawable.ic_batt_l0)
- socBackground!!.setTint(Color.GRAY)
+ val greyBlue = ContextCompat.getColor(requireContext(), R.color.colorGreyBlue)
+ socBackground!!.setTint(greyBlue)
socBattLayers += socBackground
// Get icon scaling offsets in display density:
@@ -244,7 +245,7 @@ class ChargingFragment : BaseFragment(), OnResultCommandListener {
} else if (soc <= 20) {
layer1Drawable.setTint(Color.YELLOW)
} else {
- layer1Drawable.setTint(Color.WHITE)
+ layer1Drawable.setTint(ContextCompat.getColor(requireContext(), R.color.colorDarkgreen))
}
socBattLayers += layer1Drawable
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ClimateFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ClimateFragment.kt
index d1b46b9b..da003e86 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ClimateFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ClimateFragment.kt
@@ -12,6 +12,7 @@ import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.openvehicles.OVMS.R
@@ -118,14 +119,17 @@ class ClimateFragment : BaseFragment(), OnResultCommandListener {
insideTempText.alpha = 0.6f
}
+ val errorColor = ContextCompat.getColor(requireContext(), R.color.colorTextError)
+ val warningColor = ContextCompat.getColor(requireContext(), R.color.chargeOtherColor)
+
if (dataStale == CarData.DataStale.NoValue) {
- staleLabel.setTextColor(Color.RED)
+ staleLabel.setTextColor(errorColor)
staleLabel.text = getString(R.string.no_data).lowercase()
staleLabel.visibility = View.VISIBLE
}
if (dataStale == CarData.DataStale.Stale) {
- staleLabel.setTextColor(Color.YELLOW)
+ staleLabel.setTextColor(warningColor)
staleLabel.text = getString(R.string.stale_data).lowercase()
staleLabel.visibility = View.VISIBLE
}
@@ -139,7 +143,7 @@ class ClimateFragment : BaseFragment(), OnResultCommandListener {
if (minutes > 0L) {
staleLabel.visibility = View.VISIBLE
- staleLabel.setTextColor(Color.YELLOW)
+ staleLabel.setTextColor(warningColor)
val periodText: String
if (minutes == 1L) {
periodText = getText(R.string.min1).toString()
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ControlsFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ControlsFragment.kt
index d14664d9..cac92d3f 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ControlsFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/ControlsFragment.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -14,17 +15,25 @@ import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import com.google.android.material.color.MaterialColors
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.VERTICAL
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import androidx.core.view.isVisible
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.materialswitch.MaterialSwitch
+import com.google.android.material.textfield.TextInputEditText
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.api.CommandActivity
import com.openvehicles.OVMS.api.OnResultCommandListener
import com.openvehicles.OVMS.entities.CarData
import com.openvehicles.OVMS.entities.CarData.DataStale
import com.openvehicles.OVMS.ui.BaseFragment
+import com.openvehicles.OVMS.ui.utils.Ui
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.ui2.components.quickactions.ChargingQuickAction
import com.openvehicles.OVMS.ui2.components.quickactions.CarInfoQuickAction
@@ -43,6 +52,8 @@ import com.openvehicles.OVMS.ui2.components.quickactions.adapters.QuickActionsAd
import com.openvehicles.OVMS.ui2.rendering.CarRenderingUtils
import com.openvehicles.OVMS.utils.CarsStorage
import com.openvehicles.OVMS.utils.CarsStorage.getLastSelectedCarId
+import java.text.DateFormat
+import java.util.Date
class ControlsFragment : BaseFragment(), OnResultCommandListener {
@@ -53,6 +64,9 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
private lateinit var bottomActionsAdapter: QuickActionsAdapter
private lateinit var centerActionsAdapter: QuickActionsAdapter
private lateinit var appPrefs: AppPrefs
+ private var tpmsConfig: MutableMap = mutableMapOf()
+ private var lastConfigVid: String? = null
+ private var isLoadingConfig = false
override fun onCreateView(
@@ -89,44 +103,221 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
initialiseSideActions(carData)
initialiseMainActions(carData)
initialiseCarRendering(carData)
+ loadTPMSConfig()
+
+ findViewById(R.id.btnEditTPMSConfig).setOnClickListener {
+ showEditTPMSConfigDialog()
+ }
+
+ // Setup collapsible TPMS Config header
+ val tpmsConfigHeader = findViewById(R.id.tpmsConfigHeader)
+ val tpmsConfigBody = findViewById(R.id.tpmsConfigBody)
+ val tpmsConfigChevron = findViewById(R.id.tpmsConfigChevron)
+
+ tpmsConfigHeader.setOnClickListener {
+ val vehicleId = carData?.sel_vehicleid
+ val isExpanded = tpmsConfigBody.visibility == View.VISIBLE
+ val nextVis = if (isExpanded) View.GONE else View.VISIBLE
+
+ tpmsConfigBody.visibility = nextVis
+ tpmsConfigChevron.rotation = if (isExpanded) 0f else 90f
+
+ appPrefs.saveData("pref_controls_tpms_config_expanded_$vehicleId", if (nextVis == View.VISIBLE) "1" else "0")
+ }
+ }
+
+ override fun update(carData: CarData?) {
+ val carIdChanged = this.carData?.sel_vehicleid != carData?.sel_vehicleid
+ this.carData = carData
+ updateServiceInfo(carData)
+ updateTPMSData(carData)
+ if (carIdChanged) {
+ tpmsConfig.clear()
+ updateTPMSConfigUI()
+ loadTPMSConfig()
+ }
+ sideActionsAdapter.setCarData(carData)
+ centerActionsAdapter.setCarData(carData)
+ sideActionsAdapter.notifyDataSetChanged()
+ centerActionsAdapter.notifyDataSetChanged()
+ initialiseCarRendering(carData)
+ }
+
+ private fun loadTPMSConfig() {
+ val vid = carData?.sel_vehicleid
+ if (carData?.car_type != "SQ" || vid == null) {
+ findViewById(R.id.cardTPMSConfig).visibility = View.GONE
+ return
+ }
+
+ if (isLoadingConfig && lastConfigVid == vid) return
+
+ isLoadingConfig = true
+ lastConfigVid = vid
+
+ // Use prefix 7 (command with output) instead of 1
+ sendCommand("", "7,config list xsq", object : OnResultCommandListener {
+ override fun onResultCommand(result: Array) {
+ isLoadingConfig = false
+ if (result.size > 2 && result[1] == "0") {
+ parseTPMSConfig(result[2])
+ updateTPMSConfigUI()
+ } else {
+ // Try without prefix if 7 fails, or maybe it's just slow
+ Log.w("ControlsFragment", "TPMS Config load failed with code ${result.getOrNull(1)}")
+ }
+ }
+ })
+ }
+
+ private fun parseTPMSConfig(output: String) {
+ tpmsConfig.clear()
+ val lines = output.lines()
+ for (line in lines) {
+ // More robust parsing: split at first colon, trim both sides
+ val parts = line.trim().split(":", limit = 2)
+ if (parts.size == 2) {
+ val key = parts[0].trim()
+ val value = parts[1].trim()
+ if (key.isNotEmpty()) {
+ tpmsConfig[key] = value
+ }
+ }
+ }
+ }
+
+ private fun updateTPMSConfigUI() {
+ if (view == null) return
+ val summaryView = findViewById(R.id.tpmsConfigSummary) as TextView
+ val card = findViewById(R.id.cardTPMSConfig)
+ val body = findViewById(R.id.tpmsConfigBody)
+ val chevron = findViewById(R.id.tpmsConfigChevron) as ImageView
+
+ if (tpmsConfig.isEmpty()) {
+ card.visibility = View.GONE
+ return
+ }
+
+ val vehicleId = getLastSelectedCarId()
+ val showTPMS = appPrefs.getData("pref_controls_show_tpms_$vehicleId", "0") == "1"
+ card.visibility = if (showTPMS) View.VISIBLE else View.GONE
+
+ // Restore expanded state
+ val isExpanded = appPrefs.getData("pref_controls_tpms_config_expanded_$vehicleId", "0") == "1"
+ body.visibility = if (isExpanded) View.VISIBLE else View.GONE
+ chevron.rotation = if (isExpanded) 90f else 0f
+
+ val alert = tpmsConfig["tpms.alert.enable"] ?: "no"
+ val front = tpmsConfig["tpms.front.pressure"] ?: "0"
+ val rear = tpmsConfig["tpms.rear.pressure"] ?: "0"
+ val temp = tpmsConfig["tpms.temp"] ?: "no"
+ val warnVal = tpmsConfig["tpms.value.warn"] ?: "0"
+ val alertVal = tpmsConfig["tpms.value.alert"] ?: "0"
+
+ summaryView.text = "\nAlert message: $alert \n\nShow temperatures: $temp \n\nTarget pressure:\n Front: $front kPa • Rear: $rear kPa \n\nDifference from target pressure:\n Warn: $warnVal kPa • Alert: $alertVal kPa"
+ }
+
+ private fun showEditTPMSConfigDialog() {
+ val dialogView = layoutInflater.inflate(R.layout.dialog_edit_tpms_config, null)
+ val swAlert = dialogView.findViewById(R.id.swAlertEnable)
+ val etFront = dialogView.findViewById(R.id.etFrontPressure)
+ val etRear = dialogView.findViewById(R.id.etRearPressure)
+ val swTemp = dialogView.findViewById(R.id.swTempEnable)
+ val etWarn = dialogView.findViewById(R.id.etWarnValue)
+ val etAlert = dialogView.findViewById(R.id.etAlertValue)
+
+ swAlert.isChecked = tpmsConfig["tpms.alert.enable"] == "yes"
+ etFront.setText(tpmsConfig["tpms.front.pressure"] ?: "0")
+ etRear.setText(tpmsConfig["tpms.rear.pressure"] ?: "0")
+ swTemp.isChecked = tpmsConfig["tpms.temp"] == "yes"
+ etWarn.setText(tpmsConfig["tpms.value.warn"] ?: "0")
+ etAlert.setText(tpmsConfig["tpms.value.alert"] ?: "0")
+
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle("TPMS Config (XSQ)")
+ .setView(dialogView)
+ .setPositiveButton(R.string.Save) { _, _ ->
+ val newConfig = mapOf(
+ "tpms.alert.enable" to if (swAlert.isChecked) "yes" else "no",
+ "tpms.front.pressure" to etFront.text.toString(),
+ "tpms.rear.pressure" to etRear.text.toString(),
+ "tpms.temp" to if (swTemp.isChecked) "yes" else "no",
+ "tpms.value.warn" to etWarn.text.toString(),
+ "tpms.value.alert" to etAlert.text.toString()
+ )
+ saveTPMSConfig(newConfig)
+ }
+ .setNegativeButton(R.string.Cancel, null)
+ .show()
+ }
+
+ private fun saveTPMSConfig(newConfig: Map) {
+ val commands = mutableListOf()
+ for ((key, value) in newConfig) {
+ if (tpmsConfig[key] != value) {
+ commands.add("7,config set xsq $key $value")
+ }
+ }
+
+ if (commands.isEmpty()) return
+
+ // Send commands one by one
+ for (cmd in commands) {
+ sendCommand("Saving...", cmd, null)
+ }
+
+ // Give it a short delay and reload
+ view?.postDelayed({ loadTPMSConfig() }, 1500)
}
private fun updateServiceInfo(carData: CarData?) {
// Show known car service interval info:
val serviceBtn= findViewById(R.id.serviceToggle) as ExtendedFloatingActionButton
val serviceTextView = findViewById(R.id.serviceinfo) as TextView
+ val serviceCard = findViewById(R.id.cardServiceInfo) as View?
+
+ val vehicleId = carData?.sel_vehicleid
- var serviceInfo = ""
+ val serviceInfoParts = mutableListOf()
if (carData!!.car_servicerange >= 0) {
- serviceInfo += String.format("%d km", carData.car_servicerange)
+ serviceInfoParts.add(String.format("%d km", carData.car_servicerange))
}
if (carData.car_servicetime >= 0) {
- if (serviceInfo != "") {
- serviceInfo += "\n"
- }
val now = System.currentTimeMillis() / 1000
val serviceDays = (carData.car_servicetime - now) / 86400
- var daystring = getString(R.string.ndays)
- serviceInfo += String.format(daystring, serviceDays)
+
+ var timeInfo = String.format(getString(R.string.ndays), serviceDays)
+ if (serviceDays > 0) {
+ val date = Date(carData.car_servicetime.toLong() * 1000)
+ val dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM)
+ timeInfo += " (${dateFormat.format(date)})"
+ }
+ serviceInfoParts.add(timeInfo)
}
if (carData.car_gen_substate != "" && carData.car_type == "SQ") {
- var servicelvl = carData.car_gen_substate
- serviceInfo += "\n"
- serviceInfo += String.format(" %s", servicelvl)
+ serviceInfoParts.add(carData.car_gen_substate)
}
- if (serviceInfo == "") {
+
+ if (serviceInfoParts.isEmpty()) {
serviceBtn.visibility = View.INVISIBLE
serviceBtn.isEnabled = false
- serviceTextView.visibility = View.INVISIBLE
+ serviceCard?.visibility = View.GONE
} else {
serviceBtn.visibility = View.VISIBLE
- serviceTextView.text = serviceInfo
+ // Join parts with a bullet or separator to keep them side by side
+ serviceTextView.text = serviceInfoParts.joinToString(" • ")
+
+ // Apply persisted visibility
+ val showService = appPrefs.getData("pref_controls_show_service_$vehicleId", "0") == "1"
+ serviceCard?.isVisible = showService
}
serviceBtn.setOnClickListener {
// Toggle visibility
- serviceTextView.visibility =
- if (serviceTextView.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
+ serviceCard?.let { card ->
+ card.isVisible = !card.isVisible
+ appPrefs.saveData("pref_controls_show_service_$vehicleId", if (card.isVisible) "1" else "0")
+ }
}
if(carData.car_type == "SQ") {
@@ -149,9 +340,16 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
val flTPMS = findViewById(R.id.tpmsFL) as TextView
val rrTPMS = findViewById(R.id.tpmsRR) as TextView
val rlTPMS = findViewById(R.id.tpmsRL) as TextView
+ val cardFL = findViewById(R.id.cardTPMS_FL) as View?
+ val cardFR = findViewById(R.id.cardTPMS_FR) as View?
+ val cardRL = findViewById(R.id.cardTPMS_RL) as View?
+ val cardRR = findViewById(R.id.cardTPMS_RR) as View?
+ val cardConfig = findViewById(R.id.cardTPMSConfig) as View?
val staleTPMS = findViewById(R.id.tpmsStale) as TextView
val tpmsFAB = findViewById(R.id.tpmsToggle) as ExtendedFloatingActionButton
+ val vehicleId = getLastSelectedCarId()
+
// Disable TPMS for vehicles not supporting any:
if (carData?.car_type in listOf("RT", "EN", "NRJK")) {
tpmsFAB.isEnabled = false
@@ -159,17 +357,29 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
return
}
+ // Apply persisted visibility
+ val showTPMS = appPrefs.getData("pref_controls_show_tpms_$vehicleId", "0") == "1"
+ val initialVis = if (showTPMS) View.VISIBLE else View.GONE
+ cardFL?.visibility = initialVis
+ cardFR?.visibility = initialVis
+ cardRL?.visibility = initialVis
+ cardRR?.visibility = initialVis
+ staleTPMS.visibility = initialVis
+ if (carData?.car_type == "SQ" && !tpmsConfig.isEmpty()) {
+ cardConfig?.visibility = initialVis
+ }
+
tpmsFAB.setOnClickListener {
- frTPMS.visibility =
- if (frTPMS.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
- flTPMS.visibility =
- if (flTPMS.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
- rrTPMS.visibility =
- if (rrTPMS.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
- rlTPMS.visibility =
- if (rlTPMS.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
- staleTPMS.visibility =
- if (staleTPMS.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
+ val nextVis = if (cardFR?.visibility == View.VISIBLE) View.GONE else View.VISIBLE
+ cardFL?.visibility = nextVis
+ cardFR?.visibility = nextVis
+ cardRL?.visibility = nextVis
+ cardRR?.visibility = nextVis
+ staleTPMS.visibility = nextVis
+ if (carData?.car_type == "SQ" && !tpmsConfig.isEmpty()) {
+ cardConfig?.visibility = nextVis
+ }
+ appPrefs.saveData("pref_controls_show_tpms_$vehicleId", if (nextVis == View.VISIBLE) "1" else "0")
}
// Long click to open TPMS details via CommandActivity
@@ -184,8 +394,6 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
true
}
- val vehicleId = getLastSelectedCarId()
-
// Check if new TPMS data (msg code 'Y') is available
val hasNewTPMS = carData?.car_tpms_wheelname != null && carData.car_tpms_wheelname!!.isNotEmpty()
@@ -396,8 +604,12 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
rrTPMS.text = buildTPMSDisplay(3)
// Apply alert colors
- val alertColors = intArrayOf(Color.WHITE, Color.YELLOW, Color.RED)
- val defaultColor = context?.resources?.getColor(R.color.colorAccent) ?: Color.WHITE
+ val alertColors = intArrayOf(
+ MaterialColors.getColor(requireContext(), android.R.attr.textColorPrimary, Color.BLACK),
+ ContextCompat.getColor(requireContext(), R.color.chargeOtherColor),
+ ContextCompat.getColor(requireContext(), R.color.colorTextError)
+ )
+ val defaultColor = ContextCompat.getColor(requireContext(), R.color.colorAccent)
flTPMS.setTextColor(if (getAlertLevel(0) > 0) alertColors[getAlertLevel(0)] else defaultColor)
frTPMS.setTextColor(if (getAlertLevel(1) > 0) alertColors[getAlertLevel(1)] else defaultColor)
@@ -414,17 +626,20 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
else -> DataStale.NoValue
}
+ val errorColor = ContextCompat.getColor(requireContext(), R.color.colorTextError)
+ val warningColor = ContextCompat.getColor(requireContext(), R.color.chargeOtherColor)
+
when (stale1) {
DataStale.NoValue -> {
- staleTPMS.setTextColor(Color.RED)
+ staleTPMS.setTextColor(errorColor)
staleTPMS.text = getString(R.string.no_data).lowercase()
}
DataStale.Stale -> {
- staleTPMS.setTextColor(Color.YELLOW)
+ staleTPMS.setTextColor(warningColor)
staleTPMS.text = getString(R.string.stale_data).lowercase()
}
DataStale.Good -> {
- context?.resources?.getColor(R.color.colorAccent)?.let { staleTPMS.setTextColor(it) }
+ staleTPMS.setTextColor(defaultColor)
staleTPMS.text = getString(R.string.latest_data).lowercase()
}
}
@@ -437,7 +652,7 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
val days = minutes / (60 * 24)
if (minutes > 0L) {
- staleTPMS.setTextColor(Color.YELLOW)
+ staleTPMS.setTextColor(warningColor)
val periodText = when {
minutes == 1L -> getText(R.string.min1).toString()
days > 1 -> String.format(getText(R.string.ndays).toString(), days)
@@ -449,7 +664,9 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
// Auto-expand TPMS display if any alert is active
if ((0..3).any { getAlertLevel(it) > 0 }) {
- tpmsFAB.toggle()
+ if (cardFR?.visibility != View.VISIBLE) {
+ tpmsFAB.performClick()
+ }
}
}
@@ -511,15 +728,6 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
centerActionsAdapter.notifyDataSetChanged()
}
- override fun update(carData: CarData?) {
- this.carData = carData
- updateTPMSData(carData)
- sideActionsAdapter.setCarData(carData)
- centerActionsAdapter.setCarData(carData)
- sideActionsAdapter.notifyDataSetChanged()
- centerActionsAdapter.notifyDataSetChanged()
- initialiseCarRendering(carData)
- }
override fun onResultCommand(result: Array) {
if (result.size <= 1) return
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/EnergyFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/EnergyFragment.kt
index 5682d78f..715f2963 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/EnergyFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/EnergyFragment.kt
@@ -76,7 +76,8 @@ class EnergyFragment : BaseFragment(), OnResultCommandListener, EnergyMetricsAda
var socBattLayers = emptyList().toMutableList()
val socBackground = ContextCompat.getDrawable(requireContext(), R.drawable.ic_batt_l0)
- socBackground!!.setTint(Color.GRAY)
+ val greyBlue = ContextCompat.getColor(requireContext(), R.color.colorGreyBlue)
+ socBackground!!.setTint(greyBlue)
socBattLayers += socBackground
// Get icon scaling offsets in display density:
@@ -104,7 +105,7 @@ class EnergyFragment : BaseFragment(), OnResultCommandListener, EnergyMetricsAda
} else if (soc <= 20) {
layer1Drawable.setTint(Color.YELLOW)
} else {
- layer1Drawable.setTint(Color.WHITE)
+ layer1Drawable.setTint(ContextCompat.getColor(requireContext(), R.color.colorDarkgreen))
}
socBattLayers += layer1Drawable
}
@@ -112,6 +113,7 @@ class EnergyFragment : BaseFragment(), OnResultCommandListener, EnergyMetricsAda
val socBattLayer = LayerDrawable(socBattLayers.toTypedArray())
socBattIcon.setImageDrawable(socBattLayer)
socTextTitle.text = getString(R.string.battery_data_soc_2)
+ socTextTitle.setTextColor(greyBlue)
socText.text = carData?.car_soc
var showSoc = true
socText.setOnClickListener {
@@ -120,6 +122,9 @@ class EnergyFragment : BaseFragment(), OnResultCommandListener, EnergyMetricsAda
socTextTitle.text = if (!showSoc) getString(R.string.EstimatedShort) else getString(R.string.battery_data_soc_2)
}
+ // Battery SOH
+ battSohTitle.setTextColor(greyBlue)
+
// Battery temp
val batteryTemp = findViewById(R.id.battTemp) as TextView
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/HomeFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/HomeFragment.kt
index bcbd0fdf..2e48e447 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/HomeFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/HomeFragment.kt
@@ -1,6 +1,9 @@
package com.openvehicles.OVMS.ui2.pages
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
import android.content.Context
+import android.content.res.ColorStateList
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.graphics.Bitmap
@@ -105,6 +108,7 @@ import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch
import android.location.Address
import androidx.appcompat.view.ActionMode
+import com.google.android.material.color.MaterialColors
import com.openvehicles.OVMS.utils.Base64
/**
@@ -123,6 +127,8 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
private lateinit var quickActionsEditorAdapter: QuickActionsEditorAdapter
private lateinit var tabsAdapter: HomeTabsAdapter
private var textColor by Delegates.notNull()
+ private var batteryAnimator: ObjectAnimator? = null
+ private var chargingCardAnimator: ValueAnimator? = null
private lateinit var appPrefs: AppPrefs
@@ -206,6 +212,10 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
}
override fun onDestroyView() {
+ batteryAnimator?.cancel()
+ batteryAnimator = null
+ chargingCardAnimator?.cancel()
+ chargingCardAnimator = null
// Ensure we remove any pending callbacks to avoid leaks when view is destroyed
view?.findViewById(R.id.carStatus)?.let { statusView ->
parkTimeRunnable?.let { statusView.removeCallbacks(it) }
@@ -402,6 +412,8 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
initialiseTabs(carData)
initialiseBottomInfo(carData)
initialiseCarDropDown()
+ gsmicon(carData)
+ gpsicon(carData)
// Provide color provider to tabs adapter
tabsAdapter.setColorProvider { tab ->
@@ -541,6 +553,9 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
View.inflate(context, android.R.layout.simple_spinner_item, null) as TextView
textView.setText(getItem(position)?.carName)
textView.textSize = 20F
+ // Use theme-aware text color
+ val color = MaterialColors.getColor(textView, android.R.attr.textColorPrimary)
+ textView.setTextColor(color)
return textView
}
@@ -556,7 +571,14 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
row = convertView
}
row?.findViewById(R.id.txt_title)?.text = getItem(position)?.carName
- row?.findViewById(R.id.img_car)?.setImageResource(getDrawableIdentifier(context, getItem(position)?.carData?.sel_vehicle_image))
+ val carData = getItem(position)?.carData
+ val imgCar = row?.findViewById(R.id.img_car)
+ val customDrawable = Ui.getCarDrawable(context, carData?.sel_vehicle_image)
+ if (customDrawable != null) {
+ imgCar?.setImageDrawable(customDrawable)
+ } else {
+ imgCar?.setImageResource(getDrawableIdentifier(context, carData?.sel_vehicle_image))
+ }
row?.findViewById(R.id.menuRange)?.text = String.format("%s, %s: %s, %s: %s", getItem(position)?.carData?.car_soc, context.getString(R.string.IdealShort).split(" ").first(), getItem(position)?.carData?.car_range_ideal, context.getString(R.string.EstimatedShort).split(" ").first(), getItem(position)?.carData?.car_range_estimated)
return row!!
}
@@ -580,8 +602,9 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
(activity as MainActivityUI2?)?.supportActionBar?.title = carData?.sel_vehicle_label
// SOC icon and label
- val socText: TextView = findViewById(R.id.battPercent) as TextView
- val rangeText: TextView = findViewById(R.id.battRange) as TextView
+ val socValueText: TextView = findViewById(R.id.battPercentValue) as TextView
+ val rangeValueText: TextView = findViewById(R.id.battRangeValue) as TextView
+ val rangeUnitText: TextView = findViewById(R.id.battRangeUnit) as TextView
val socBattIcon = findViewById(R.id.batteryIndicatorView) as ImageView
var socBattLayers = emptyList().toMutableList()
@@ -615,41 +638,88 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
} else if (soc <= 20) {
layer1Drawable.setTint(Color.YELLOW)
} else {
- layer1Drawable.setTint(Color.WHITE)
+ layer1Drawable.setTint(ContextCompat.getColor(requireContext(), R.color.colorDarkgreen))
}
socBattLayers += layer1Drawable
}
val socBattLayer = LayerDrawable(socBattLayers.toTypedArray())
socBattIcon.setImageDrawable(socBattLayer)
- socText.text = when (socState) {
- 1 -> getString(R.string.ideal_range_abbreviation, carData?.car_range_ideal)
- 2 -> getString(R.string.estimated_range_abbreviation, carData?.car_range_estimated)
- else -> carData?.car_soc
+
+ // Pulsating animation when charging
+ if (carData?.car_charging == true) {
+ if (batteryAnimator == null) {
+ batteryAnimator = ObjectAnimator.ofFloat(socBattIcon, "alpha", 1.0f, 0.4f).apply {
+ duration = 1500
+ repeatCount = ValueAnimator.INFINITE
+ repeatMode = ValueAnimator.REVERSE
+ start()
+ }
+ }
+ } else {
+ batteryAnimator?.cancel()
+ batteryAnimator = null
+ socBattIcon.alpha = 1.0f
}
- val rangeDisplay = appPrefs.appUIPrefs.getStringSet("home_range_display_mode", HashSet())
- val idealRange = rangeDisplay?.contains("ideal") == true
- val estimatedRange = rangeDisplay?.contains("estimated") == true
+ socValueText.text = carData?.car_soc?.replace("%", "")
- if (idealRange && estimatedRange) {
- rangeText.text = "${getString(R.string.ideal_range_abbreviation, carData?.car_range_ideal)}, ${getString(R.string.estimated_range_abbreviation, carData?.car_range_estimated)}"
- } else if (idealRange || estimatedRange) {
- rangeText.text = if (idealRange) carData?.car_range_ideal else carData?.car_range_estimated
- } else {
- val socToggleListener = {
- socState += 1
- if (socState > 2)
- socState = 0
- socText.text = when (socState) {
- 1 -> getString(R.string.ideal_range_abbreviation, carData?.car_range_ideal)
- 2 -> getString(R.string.estimated_range_abbreviation, carData?.car_range_estimated)
- else -> carData?.car_soc
+ val socToggleListener = {
+ socState += 1
+ val maxState = if (carData?.car_charging == true) 2 else 1
+ if (socState > maxState)
+ socState = 0
+ appPrefs.saveData("home_batt_display_mode", socState.toString())
+ update(carData)
+ }
+
+ // Load persisted mode
+ socState = appPrefs.getData("home_batt_display_mode", "0")?.toIntOrNull() ?: 0
+ if (socState == 2 && carData?.car_charging == false) {
+ socState = 0
+ }
+
+ val unit = carData?.car_distance_units ?: "km"
+ rangeValueText.text = when (socState) {
+ 0 -> {
+ // Show Range(s) based on app settings
+ val rangeDisplay = appPrefs.appUIPrefs.getStringSet("home_range_display_mode", setOf("ideal", "estimated"))
+ val showIdeal = rangeDisplay?.contains("ideal") == true
+ val showEstimated = rangeDisplay?.contains("estimated") == true
+
+ when {
+ showIdeal && showEstimated -> String.format("%.0f / %.0f", carData?.car_range_ideal_raw ?: 0f, carData?.car_range_estimated_raw ?: 0f)
+ showIdeal -> String.format("%.0f", carData?.car_range_ideal_raw ?: 0f)
+ showEstimated -> String.format("%.0f", carData?.car_range_estimated_raw ?: 0f)
+ else -> String.format("%.0f", carData?.car_range_estimated_raw ?: 0f)
}
}
- socText.setOnClickListener { socToggleListener() }
- socBattIcon.setOnClickListener { socToggleListener() }
+ 1 -> {
+ // Show Consumption
+ val used = carData?.car_energyused ?: 0f
+ val recd = carData?.car_energyrecd ?: 0f
+ val trip = carData?.car_tripmeter_raw ?: 0f
+ var consumption = (used - recd) * 1000f / (trip / 10f)
+ if (!consumption.isFinite()) consumption = 0f
+ String.format("%.1f", consumption / 10f)
+ }
+ 2 -> String.format("%.1f", carData?.car_charge_power_input_kw_raw ?: 0f)
+ else -> ""
}
+ rangeUnitText.text = when (socState) {
+ 0 -> unit
+ 1 -> "kWh/100$unit"
+ 2 -> "kW"
+ else -> ""
+ }
+
+ // Apply click listener to the entire row for easy toggling
+ findViewById(R.id.heroStatusRow).setOnClickListener { socToggleListener() }
+ socValueText.setOnClickListener { socToggleListener() }
+ socBattIcon.setOnClickListener { socToggleListener() }
+ rangeValueText.setOnClickListener { socToggleListener() }
+ rangeUnitText.setOnClickListener { socToggleListener() }
+
// Status label
val statusText: TextView = findViewById(R.id.carStatus) as TextView
@@ -666,24 +736,29 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
// ONLINE state: differentiate Driving / Charging / Parked
val isDriving = carData?.car_started == true
val isCharging = carData?.car_charging == true || carData?.car_charge_state_i_raw == 14
+ val parkIcon = view?.findViewById(R.id.parkIcon)
if (isDriving) {
// Ensure any pending parking runnable is removed
parkTimeRunnable?.let { statusText.removeCallbacks(it) }
parkTimeRunnable = null
statusText.text = carData?.car_speed
- statusText.setCompoundDrawablesWithIntrinsicBounds(0,0,0,0)
+ parkIcon?.visibility = View.GONE
statusText.setOnClickListener(null)
} else if (isCharging) {
parkTimeRunnable?.let { statusText.removeCallbacks(it) }
parkTimeRunnable = null
- statusText.setCompoundDrawablesWithIntrinsicBounds(0,0,0,0)
+ parkIcon?.visibility = View.GONE
// Charging Text is set below
} else {
// Parked -> apply display mode (immediate toggle on click)
applyParkedStatus(statusText, carData)
}
+ // Odometer display
+ val odoText = view?.findViewById(R.id.carOdometer)
+ odoText?.text = carData?.car_odometer
+
if (isCharging) {
statusText.setText(R.string.state_charging_label)
@@ -773,125 +848,128 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
// Car image
val image: ImageView = findViewById(R.id.carStatusImage) as ImageView
- val name_splitted = carData?.sel_vehicle_image?.split("_")
- val car_tire_image1 = getDrawableIdentifier(
- context,
- name_splitted?.minus(name_splitted.last())?.joinToString("_") +"_tireanim"
- )
-
var layers = emptyList()
+ val customDrawable = Ui.getCarDrawable(requireContext(), carData?.sel_vehicle_image)
- if (carData?.car_rearleftdoor_open == true || CAR_RENDER_TEST_MODE_D) {
- val modeResource = getDrawableIdentifier(
+ if (carData?.sel_vehicle_image?.startsWith("file://") == true && customDrawable != null) {
+ layers = layers.plus(customDrawable)
+ } else {
+ val name_splitted = carData?.sel_vehicle_image?.split("_")
+ val car_tire_image1 = getDrawableIdentifier(
context,
- carData?.sel_vehicle_image+"_rld"
+ name_splitted?.minus(name_splitted.last())?.joinToString("_") + "_tireanim"
)
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- }
- if (carData?.car_frontleftdoor_open == true || CAR_RENDER_TEST_MODE_D) {
- val modeResource = getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image+"_fld"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- }
-
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image
- )
- )!!)
-
- val speedShownInUI = car_tire_image1 > 0 && (carData?.car_started == true||CAR_RENDER_TEST_MODE_D)
-
- if (speedShownInUI) {
- val animationDrawable = context?.getDrawable(car_tire_image1) as AnimationDrawable
- val carAnim = CarAnimationDrawable()
- var drawables = emptyList()
- for (i in 0.. 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
}
- drawables.forEach { carAnim.addFrame(it, 35) }
- carAnim.isOneShot = false
- layers = layers.plus(carAnim)
+ if (carData?.car_frontleftdoor_open == true || CAR_RENDER_TEST_MODE_D) {
+ val modeResource = getDrawableIdentifier(
+ context,
+ carData?.sel_vehicle_image + "_fld"
+ )
+ if (modeResource > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ }
+ val carResId = getDrawableIdentifier(context, carData?.sel_vehicle_image)
+ if (carResId > 0) {
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), carResId)!!)
+ }
+ val speedShownInUI =
+ car_tire_image1 > 0 && (carData?.car_started == true || CAR_RENDER_TEST_MODE_D)
- if ((carData?.car_speed_raw ?: 0f) > 0) {
- // Adjust animaion speed
- carAnim.start()
- carAnim.setDuration((320 / (carData?.car_speed_raw!!/3.5)).toInt())
+ if (speedShownInUI) {
+ val animationDrawable =
+ AppCompatResources.getDrawable(requireContext(), car_tire_image1) as AnimationDrawable
+ val carAnim = CarAnimationDrawable()
+ var drawables = emptyList()
+ for (i in 0 until animationDrawable.numberOfFrames) {
+ drawables = drawables.plus(animationDrawable.getFrame(i))
+ }
+ drawables.forEach { carAnim.addFrame(it, 35) }
+ carAnim.isOneShot = false
+ layers = layers.plus(carAnim)
+
+ if ((carData?.car_speed_raw ?: 0f) > 0) {
+ // Adjust animation speed
+ carAnim.start()
+ carAnim.setDuration((320 / (carData?.car_speed_raw!! / 3.5)).toInt())
+ }
}
- }
-
- if (carData?.car_headlights_on == true || CAR_RENDER_TEST_MODE_HD) {
- val modeResource = getDrawableIdentifier(
- context,
- name_splitted?.minus(name_splitted.last())?.joinToString("_") +"_hd"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- }
+ if (carData?.car_headlights_on == true || CAR_RENDER_TEST_MODE_HD) {
+ val modeResource = getDrawableIdentifier(
+ context,
+ name_splitted?.minus(name_splitted.last())?.joinToString("_") + "_hd"
+ )
+ if (modeResource > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ }
- if (carData?.car_rearrightdoor_open == true || CAR_RENDER_TEST_MODE_D) {
- val modeResource = getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image+"_rrd"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- }
+ if (carData?.car_rearrightdoor_open == true || CAR_RENDER_TEST_MODE_D) {
+ val modeResource = getDrawableIdentifier(
+ context,
+ carData?.sel_vehicle_image + "_rrd"
+ )
+ if (modeResource > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ }
- if (carData?.car_frontrightdoor_open == true || CAR_RENDER_TEST_MODE_D) {
- val modeResource = getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image+"_frd"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- }
+ if (carData?.car_frontrightdoor_open == true || CAR_RENDER_TEST_MODE_D) {
+ val modeResource = getDrawableIdentifier(
+ context,
+ carData?.sel_vehicle_image + "_frd"
+ )
+ if (modeResource > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ }
- if (carData?.car_trunk_open == true || CAR_RENDER_TEST_MODE_T) {
- val modeResource = getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image+"_t"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- else {
+ if (carData?.car_trunk_open == true || CAR_RENDER_TEST_MODE_T) {
val modeResource = getDrawableIdentifier(
context,
- name_splitted?.minus(name_splitted.last())?.joinToString("_")+"_t"
+ carData?.sel_vehicle_image + "_t"
)
if (modeResource > 0)
layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ else {
+ val modeRes = getDrawableIdentifier(
+ context,
+ name_splitted?.minus(name_splitted.last())?.joinToString("_") + "_t"
+ )
+ if (modeRes > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeRes)!!)
+ }
}
- }
- if (carData?.car_bonnet_open == true || CAR_RENDER_TEST_MODE_T) {
- val modeResource = getDrawableIdentifier(
- context,
- carData?.sel_vehicle_image+"_b"
- )
- if (modeResource > 0)
- layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
- else {
+ if (carData?.car_bonnet_open == true || CAR_RENDER_TEST_MODE_T) {
val modeResource = getDrawableIdentifier(
context,
- name_splitted?.minus(name_splitted.last())?.joinToString("_")+"_b"
+ carData?.sel_vehicle_image + "_b"
)
if (modeResource > 0)
layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
+ else {
+ val modeRes = getDrawableIdentifier(
+ context,
+ name_splitted?.minus(name_splitted.last())?.joinToString("_") + "_b"
+ )
+ if (modeRes > 0)
+ layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeRes)!!)
+ }
}
}
- var chargingResName = name_splitted?.minus(name_splitted.last())?.joinToString("_")
+ val name_splitted = carData?.sel_vehicle_image?.split("_")
+ var chargingResName = name_splitted?.minus(name_splitted?.last() ?: "")?.joinToString("_")
if (carData?.car_charge_mode == "performance" || (carData?.car_charge_current_raw ?: 0f) > 48) {
chargingResName += "_q"
@@ -928,6 +1006,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
}
+ /* Deactivated for artifact diagnosis
if (carData?.car_charge_state_i_raw == 0x01 || carData?.car_charge_state_i_raw == 0x02 || carData?.car_charge_state_i_raw == 0x0f || carData?.car_charging == true || CAR_RENDER_TEST_MODE_CHG) {
val modeResource = getDrawableIdentifier(
context,
@@ -936,6 +1015,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
if (modeResource > 0)
layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
}
+ */
val newDrawable = LayerDrawable(layers.toTypedArray())
@@ -949,39 +1029,32 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
override fun onAnimationRepeat(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {}
})
- image.startAnimation(anim_in)
- lastPresentCarId = carData?.sel_vehicleid
- } else if (speedShownInUI) {
- image.setImageDrawable(
- newDrawable
- )
- }
+ // image.startAnimation(anim_in)
+ lastPresentCarId = carData?.sel_vehicleid
+ } else {
+ image.setImageDrawable(
+ newDrawable
+ )
}
+}
+
// Helper to show parked status & toggle on click between simple label and duration display
private fun applyParkedStatus(statusText: TextView, carData: CarData?) {
val showDuration = appPrefs.getData("pref_show_parktime", "0") == "1"
+ val parkIcon = view?.findViewById(R.id.parkIcon)
- // Remove any previous runnable if present
- parkTimeRunnable?.let { statusText.removeCallbacks(it) }
+ // Remove any previous runnable if present
+ parkTimeRunnable?.let { statusText.removeCallbacks(it) }
if (!showDuration) {
// Show plain "parked" label only
statusText.setText(R.string.parked)
- statusText.setCompoundDrawablesWithIntrinsicBounds(0,0,0,0)
- statusText.compoundDrawablePadding = 0
+ parkIcon?.visibility = View.GONE
parkTimeRunnable = null
} else {
// Show icon + duration
- val icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_p_framed)
- statusText.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
- // Space between icon and text = width of two spaces
- try {
- val twoSpacesWidth = statusText.paint.measureText(" ") // two spaces
- statusText.compoundDrawablePadding = twoSpacesWidth.toInt()
- } catch (_: Exception) {
- statusText.compoundDrawablePadding = (statusText.resources.displayMetrics.density * 8).toInt() // Fallback ~8dp
- }
+ parkIcon?.visibility = VISIBLE
// Server-provided elapsed parking time in seconds (authoritative) -> convert to ms
val baseElapsedMs = (carData?.car_parking_timer_raw ?: 0L)
@@ -1035,6 +1108,33 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
val chargePortOpen = if (carData?.car_type == "NL") (carData.car_charging || carData.car_charge_timer) else carData?.car_chargeport_open == true
chargingCard.visibility = if (chargePortOpen == true) VISIBLE else View.GONE
+ val flowIndicator = findViewById(R.id.chargingFlowIndicator) as View?
+ flowIndicator?.visibility = if (carData?.car_charging == true) VISIBLE else View.GONE
+
+ // Glow effect for charging card
+ if (carData?.car_charging == true) {
+ chargingCard.strokeColor = ContextCompat.getColor(requireContext(), R.color.colorAccent)
+ if (chargingCardAnimator == null) {
+ chargingCardAnimator = ValueAnimator.ofFloat(0.5f, 4f).apply {
+ duration = 1200
+ repeatCount = ValueAnimator.INFINITE
+ repeatMode = ValueAnimator.REVERSE
+ addUpdateListener { animator ->
+ chargingCard.strokeWidth = TypedValue.applyDimension(
+ COMPLEX_UNIT_DIP,
+ animator.animatedValue as Float,
+ resources.displayMetrics
+ ).toInt()
+ }
+ start()
+ }
+ }
+ } else {
+ chargingCardAnimator?.cancel()
+ chargingCardAnimator = null
+ chargingCard.strokeWidth = 0
+ }
+
val ampLimitSlider = findViewById(R.id.seekBar) as RangeSlider
if ((carData?.car_charge_currentlimit_raw ?: 0f) > 31f) {
@@ -1685,7 +1785,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
if (carData?.car_gsmlock?.isNotEmpty() == true) {
if (baseInfoBuilder.isNotEmpty()) baseInfoBuilder.append("\n")
- baseInfoBuilder.append("GSM: ${carData.car_gsmlock} ${carData.car_mdm_mode}")
+ baseInfoBuilder.append("Network: ${carData.car_gsmlock} \nMode: ${carData.car_mdm_mode} ${carData.car_gsm_signal}")
}
if (carData?.car_vin?.isNotEmpty() == true) {
@@ -1705,23 +1805,34 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
private fun gsmicon(carData: CarData?) {
// GSM Bar
+ val gsmimg = findViewById(R.id.gsmView) as ImageView
if (appPrefs.getData("gsm_icon", "off") == "on") {
- val gsmimg = findViewById(R.id.gsmView) as ImageView
gsmimg.visibility = VISIBLE
- gsmimg.setImageResource(
- getDrawableIdentifier(
- activity,
- "signal_strength_" + carData?.car_gsm_bars
- )
+ val resId = getDrawableIdentifier(
+ requireContext(),
+ "signal_strength_" + (carData?.car_gsm_bars ?: 0)
)
+ if (resId > 0) {
+ gsmimg.setImageResource(resId)
+ } else {
+ gsmimg.setImageResource(R.drawable.signal_strength_0)
+ }
+ // Use static tint from resources to be safe
+ gsmimg.imageTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.colorText))
+ } else {
+ gsmimg.visibility = View.GONE
}
}
private fun gpsicon(carData: CarData?) {
// GPS Bar
+ val gpsimg = findViewById(R.id.gpsView) as ImageView
if (appPrefs.getData("gps_icon", "off") == "on") {
- val gpsimg = findViewById(R.id.gpsView) as ImageView
- gpsimg.visibility = if(carData?.car_gpslock == true) VISIBLE else View.INVISIBLE
+ gpsimg.visibility = VISIBLE
+ gpsimg.alpha = if(carData?.car_gpslock == true) 1.0f else 0.3f
+ gpsimg.imageTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.colorText))
+ } else {
+ gpsimg.visibility = View.GONE
}
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/AppUISettingsFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/AppUISettingsFragment.kt
index 5c8f5cf9..f9c29036 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/AppUISettingsFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/AppUISettingsFragment.kt
@@ -1,5 +1,7 @@
package com.openvehicles.OVMS.ui2.pages.settings
+import com.openvehicles.OVMS.ui2.misc.ThemeMode
+import androidx.preference.ListPreference
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
@@ -15,6 +17,7 @@ import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.ui.utils.Ui
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.entities.CarData
+import com.openvehicles.OVMS.ui.MainActivity
import com.openvehicles.OVMS.utils.CarsStorage
import com.openvehicles.OVMS.utils.Sys
import com.openvehicles.OVMS.utils.CarsStorage.getLastSelectedCarId
@@ -43,6 +46,18 @@ class AppUISettingsFragment: PreferenceFragmentCompat() {
carData = CarsStorage.getSelectedCarData()
val appPrefs = AppPrefs(requireContext(), "ovms")
+ // Light / dark / follow system. Stored in the "ovms" prefs by ThemeMode, so the
+ // ListPreference (which writes to the default prefs) is mirrored across and the
+ // new mode is applied right away - the Activity recreates itself.
+ findPreference(ThemeMode.PREF_KEY)?.let { pref ->
+ pref.value = ThemeMode.read(requireContext())
+ pref.onPreferenceChangeListener =
+ OnPreferenceChangeListener { _, newValue ->
+ ThemeMode.applyAndStore(requireContext(), newValue as String)
+ true
+ }
+ }
+
val home_range_display_mode_preference = findPreference("home_range_display_mode")
home_range_display_mode_preference?.setSummaryFromValues(home_range_display_mode_preference.values.toTypedArray())
home_range_display_mode_preference?.onPreferenceChangeListener = object : OnPreferenceChangeListener {
@@ -171,7 +186,14 @@ class AppUISettingsFragment: PreferenceFragmentCompat() {
oldUIPreference?.isChecked = appPrefs.getData("option_oldui_enabled", "0") == "1"
oldUIPreference?.onPreferenceChangeListener =
OnPreferenceChangeListener { preference, newValue ->
- appPrefs.saveData("option_oldui_enabled", if (newValue as Boolean) "1" else "0")
+ val enabled = newValue as Boolean
+ appPrefs.saveData("option_oldui_enabled", if (enabled) "1" else "0")
+ if (enabled) {
+ // Switch to old UI immediately:
+ val intent = Intent(requireContext(), MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ startActivity(intent)
+ }
true
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/CarEditorFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/CarEditorFragment.kt
index 82a0be17..e19b804a 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/CarEditorFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/CarEditorFragment.kt
@@ -16,6 +16,9 @@ import android.widget.FrameLayout
import android.widget.Gallery
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
+import androidx.core.view.MenuHost
+import androidx.core.view.MenuProvider
+import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.MaterialAutoCompleteTextView
@@ -34,7 +37,11 @@ import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.CarsStorage.getSelectedCarData
import com.openvehicles.OVMS.utils.CarsStorage.getStoredCars
import com.openvehicles.OVMS.utils.CarsStorage.saveStoredCars
-
+import androidx.activity.result.contract.ActivityResultContracts
+import com.google.android.material.button.MaterialButton
+import java.io.File
+import java.io.FileOutputStream
+import com.openvehicles.OVMS.ui.utils.Ui.getCarDrawable
class CarEditorFragment : BaseFragment() {
@@ -48,6 +55,128 @@ class CarEditorFragment : BaseFragment() {
private lateinit var gcmSenders: Array
private var server: EditText? = null
private var gcmSender: EditText? = null
+
+ private var customImagePath: String? = null
+ private var btnCustomImage: MaterialButton? = null
+ private var btnClearCustomImage: MaterialButton? = null
+ private var imgCustomPreview: ImageView? = null
+
+ private var customImagePathOl: String? = null
+ private var btnCustomImageOl: MaterialButton? = null
+ private var btnClearCustomImageOl: MaterialButton? = null
+ private var imgCustomPreviewOl: ImageView? = null
+ private var galleryCarOl: Gallery? = null
+
+ private var customImagePathMap: String? = null
+ private var btnCustomImageMap: MaterialButton? = null
+ private var btnClearCustomImageMap: MaterialButton? = null
+ private var imgCustomPreviewMap: ImageView? = null
+ private var galleryCarMap: Gallery? = null
+
+ private val pickImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
+ uri?.let {
+ try {
+ val inputStream = requireContext().contentResolver.openInputStream(it)
+ val fileName = "car_custom_${System.currentTimeMillis()}.png"
+ val file = File(requireContext().filesDir, fileName)
+ val outputStream = FileOutputStream(file)
+ inputStream?.copyTo(outputStream)
+ inputStream?.close()
+ outputStream.close()
+
+ customImagePath = "file://" + file.absolutePath
+ updateCustomImageUI()
+ Log.d(TAG, "PickImage: saved to $customImagePath")
+ } catch (e: Exception) {
+ Log.e(TAG, "PickImage failed", e)
+ }
+ }
+ }
+
+ private val pickOlImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
+ uri?.let {
+ try {
+ val inputStream = requireContext().contentResolver.openInputStream(it)
+ val fileName = "car_custom_ol_${System.currentTimeMillis()}.png"
+ val file = File(requireContext().filesDir, fileName)
+ val outputStream = FileOutputStream(file)
+ inputStream?.copyTo(outputStream)
+ inputStream?.close()
+ outputStream.close()
+
+ customImagePathOl = "file://" + file.absolutePath
+ updateCustomOlImageUI()
+ Log.d(TAG, "PickOlImage: saved to $customImagePathOl")
+ } catch (e: Exception) {
+ Log.e(TAG, "PickOlImage failed", e)
+ }
+ }
+ }
+
+ private val pickMapImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
+ uri?.let {
+ try {
+ val inputStream = requireContext().contentResolver.openInputStream(it)
+ val fileName = "car_custom_map_${System.currentTimeMillis()}.png"
+ val file = File(requireContext().filesDir, fileName)
+ val outputStream = FileOutputStream(file)
+ inputStream?.copyTo(outputStream)
+ inputStream?.close()
+ outputStream.close()
+
+ customImagePathMap = "file://" + file.absolutePath
+ updateCustomMapImageUI()
+ Log.d(TAG, "PickMapImage: saved to $customImagePathMap")
+ } catch (e: Exception) {
+ Log.e(TAG, "PickMapImage failed", e)
+ }
+ }
+ }
+
+ private fun updateCustomImageUI() {
+ if (customImagePath != null) {
+ btnClearCustomImage?.visibility = View.VISIBLE
+ imgCustomPreview?.visibility = View.VISIBLE
+ imgCustomPreview?.setImageDrawable(getCarDrawable(requireContext(), customImagePath))
+ galleryCar?.alpha = 0.3f
+ galleryCar?.isEnabled = false
+ } else {
+ btnClearCustomImage?.visibility = View.GONE
+ imgCustomPreview?.visibility = View.GONE
+ galleryCar?.alpha = 1.0f
+ galleryCar?.isEnabled = true
+ }
+ }
+
+ private fun updateCustomOlImageUI() {
+ if (customImagePathOl != null) {
+ btnClearCustomImageOl?.visibility = View.VISIBLE
+ imgCustomPreviewOl?.visibility = View.VISIBLE
+ imgCustomPreviewOl?.setImageDrawable(getCarDrawable(requireContext(), customImagePathOl))
+ galleryCarOl?.alpha = 0.3f
+ galleryCarOl?.isEnabled = false
+ } else {
+ btnClearCustomImageOl?.visibility = View.GONE
+ imgCustomPreviewOl?.visibility = View.GONE
+ galleryCarOl?.alpha = 1.0f
+ galleryCarOl?.isEnabled = true
+ }
+ }
+
+ private fun updateCustomMapImageUI() {
+ if (customImagePathMap != null) {
+ btnClearCustomImageMap?.visibility = View.VISIBLE
+ imgCustomPreviewMap?.visibility = View.VISIBLE
+ imgCustomPreviewMap?.setImageDrawable(getCarDrawable(requireContext(), customImagePathMap))
+ galleryCarMap?.alpha = 0.3f
+ galleryCarMap?.isEnabled = false
+ } else {
+ btnClearCustomImageMap?.visibility = View.GONE
+ imgCustomPreviewMap?.visibility = View.GONE
+ galleryCarMap?.alpha = 1.0f
+ galleryCarMap?.isEnabled = true
+ }
+ }
override fun onCreateView(
inflater: LayoutInflater,
@@ -57,8 +186,8 @@ class CarEditorFragment : BaseFragment() {
return inflater.inflate(R.layout.fragment_careditor_v2, null)
}
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
editPosition = requireArguments().getInt("position", -1)
if (editPosition >= 0) {
@@ -90,34 +219,81 @@ class CarEditorFragment : BaseFragment() {
selectServer!!.onItemClickListener =
AdapterView.OnItemClickListener { parent, view, position, id -> setSelectedServer(position, true) }
galleryCar = requireView().findViewById(R.id.ga_car) as Gallery
- galleryCar!!.setAdapter(CarImgAdapter())
- setHasOptionsMenu(true)
- load()
- }
+ galleryCar!!.setAdapter(CarImgAdapter(availableColors))
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- inflater.inflate(R.menu.control_save_delete, menu)
- }
+ btnCustomImage = requireView().findViewById(R.id.btn_custom_image)
+ btnClearCustomImage = requireView().findViewById(R.id.btn_clear_custom_image)
+ imgCustomPreview = requireView().findViewById(R.id.img_custom_preview)
- override fun onPrepareOptionsMenu(menu: Menu) {
- Log.d("CarEditorFragment", "onPrepareOptionsMenu edit car: " + (getStoredCars().size > 1))
- menu.findItem(R.id.mi_delete).setVisible(carData != null && getStoredCars().size > 1)
- // hide control, not necessary
- menu.findItem(R.id.mi_control).setVisible(false)
- }
+ btnCustomImage?.setOnClickListener {
+ pickImageLauncher.launch("image/*")
+ }
+
+ btnClearCustomImage?.setOnClickListener {
+ customImagePath = null
+ updateCustomImageUI()
+ }
+
+ galleryCarOl = requireView().findViewById(R.id.ga_car_ol) as Gallery
+ galleryCarOl!!.setAdapter(CarImgAdapter(availableOlImages, true))
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.mi_save -> {
- save()
- true
+ btnCustomImageOl = requireView().findViewById(R.id.btn_custom_image_ol)
+ btnClearCustomImageOl = requireView().findViewById(R.id.btn_clear_custom_image_ol)
+ imgCustomPreviewOl = requireView().findViewById(R.id.img_custom_preview_ol)
+
+ btnCustomImageOl?.setOnClickListener {
+ pickOlImageLauncher.launch("image/*")
+ }
+
+ btnClearCustomImageOl?.setOnClickListener {
+ customImagePathOl = null
+ updateCustomOlImageUI()
+ }
+
+ galleryCarMap = requireView().findViewById(R.id.ga_car_map) as Gallery
+ galleryCarMap!!.setAdapter(CarImgAdapter(availableMapIcons, false))
+
+ btnCustomImageMap = requireView().findViewById(R.id.btn_custom_image_map)
+ btnClearCustomImageMap = requireView().findViewById(R.id.btn_clear_custom_image_map)
+ imgCustomPreviewMap = requireView().findViewById(R.id.img_custom_preview_map)
+
+ btnCustomImageMap?.setOnClickListener {
+ pickMapImageLauncher.launch("image/*")
+ }
+
+ btnClearCustomImageMap?.setOnClickListener {
+ customImagePathMap = null
+ updateCustomMapImageUI()
+ }
+
+ val menuHost: MenuHost = requireActivity()
+ menuHost.addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.control_save_delete, menu)
}
- R.id.mi_delete -> {
- delete()
- true
+
+ override fun onPrepareMenu(menu: Menu) {
+ Log.d("CarEditorFragment", "onPrepareMenu edit car: " + (getStoredCars().size > 1))
+ menu.findItem(R.id.mi_delete).setVisible(carData != null && getStoredCars().size > 1)
+ menu.findItem(R.id.mi_control).setVisible(false)
}
- else -> false
- }
+
+ override fun onMenuItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.mi_save -> {
+ save()
+ true
+ }
+ R.id.mi_delete -> {
+ delete()
+ true
+ }
+ else -> false
+ }
+ }
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
+
+ load()
}
private fun delete() {
@@ -187,7 +363,24 @@ class CarEditorFragment : BaseFragment() {
carData!!.sel_gcm_senderid = getValue(rootView, R.id.txt_gcm_senderid)
carData!!.sel_tls = (rootView.findViewById(R.id.chk_tls_enabled) as SwitchMaterial).isChecked
carData!!.sel_tls_trust_all = (rootView.findViewById(R.id.chk_tls_trust_all) as SwitchMaterial).isChecked
- carData!!.sel_vehicle_image = availableColors[galleryCar!!.selectedItemPosition]
+
+ if (customImagePath != null) {
+ carData!!.sel_vehicle_image = customImagePath!!
+ } else {
+ carData!!.sel_vehicle_image = availableColors[galleryCar!!.selectedItemPosition]
+ }
+
+ if (customImagePathOl != null) {
+ carData!!.sel_vehicle_image_ol = customImagePathOl!!
+ } else {
+ carData!!.sel_vehicle_image_ol = availableOlImages[galleryCarOl!!.selectedItemPosition]
+ }
+
+ if (customImagePathMap != null) {
+ carData!!.sel_vehicle_image_map = customImagePathMap!!
+ } else {
+ carData!!.sel_vehicle_image_map = availableMapIcons[galleryCarMap!!.selectedItemPosition]
+ }
} catch (e: ValidationException) {
Log.e("Validation", e.message, e)
return
@@ -240,15 +433,62 @@ class CarEditorFragment : BaseFragment() {
}
// set car image:
- var index = -1
- for (imgRes in availableColors) {
- index++
- if (imgRes == carData!!.sel_vehicle_image) {
- break
+ if (carData!!.sel_vehicle_image.startsWith("file://")) {
+ customImagePath = carData!!.sel_vehicle_image
+ updateCustomImageUI()
+ } else {
+ customImagePath = null
+ updateCustomImageUI()
+ var index = -1
+ for (imgRes in availableColors) {
+ index++
+ if (imgRes == carData!!.sel_vehicle_image) {
+ break
+ }
+ }
+ if (index >= 0) {
+ galleryCar!!.setSelection(index)
+ }
+ }
+
+ // set top-down image:
+ if (carData!!.sel_vehicle_image_ol.isNotEmpty() && carData!!.sel_vehicle_image_ol.startsWith("file://")) {
+ customImagePathOl = carData!!.sel_vehicle_image_ol
+ updateCustomOlImageUI()
+ } else {
+ customImagePathOl = null
+ updateCustomOlImageUI()
+ var index = -1
+ val targetImg = if (carData!!.sel_vehicle_image_ol.isEmpty()) carData!!.sel_vehicle_image else carData!!.sel_vehicle_image_ol
+ for (imgRes in availableOlImages) {
+ index++
+ if (imgRes == targetImg) {
+ break
+ }
+ }
+ if (index >= 0) {
+ galleryCarOl!!.setSelection(index)
}
}
- if (index >= 0) {
- galleryCar!!.setSelection(index)
+
+ // set map image:
+ if (!carData!!.sel_vehicle_image_map.isNullOrEmpty() && carData!!.sel_vehicle_image_map.startsWith("file://")) {
+ customImagePathMap = carData!!.sel_vehicle_image_map
+ updateCustomMapImageUI()
+ } else {
+ customImagePathMap = null
+ updateCustomMapImageUI()
+ var index = -1
+ val targetImg = if (carData!!.sel_vehicle_image_map.isNullOrEmpty()) "map_car_default" else carData!!.sel_vehicle_image_map
+ for (imgRes in availableMapIcons) {
+ index++
+ if (imgRes == targetImg) {
+ break
+ }
+ }
+ if (index >= 0) {
+ galleryCarMap!!.setSelection(index)
+ }
}
// save selected vehicle label:
@@ -296,6 +536,64 @@ class CarEditorFragment : BaseFragment() {
private const val TAG = "CarEditorFragment"
+ private val availableMapIcons = arrayOf(
+ "map_car_default",
+ "map_car_ampera_black",
+ "map_car_i3",
+ "map_car_imiev",
+ "map_car_ioniq5_cybergray",
+ "map_car_ioniq_polarwhite",
+ "map_car_kangoo",
+ "map_car_kiaev6_white",
+ "map_car_kianiro_grey",
+ "map_car_leaf2_gunmetallic",
+ "map_car_leaf_pearlwhite",
+ "map_car_mgzs_white",
+ "map_car_niu_mqi_gt_white",
+ "map_car_nrjk",
+ "map_car_smart",
+ "map_car_thinkcity_classicblack",
+ "map_car_twizy_snowwhitewithblack",
+ "map_car_vwup_white",
+ "map_car_zoe_white"
+ )
+
+ private val availableOlImages = arrayOf(
+ "car_roadster_arcticwhite",
+ "car_roadster_brilliantyellow",
+ "car_roadster_electricblue",
+ "car_roadster_fushionred",
+ "car_roadster_glacierblue",
+ "car_roadster_jetblack",
+ "car_roadster_lightninggreen",
+ "car_roadster_obsidianblack",
+ "car_roadster_racinggreen",
+ "car_roadster_radiantred",
+ "car_roadster_sterlingsilver",
+ "car_roadster_thundergray",
+ "car_roadster_twilightblue",
+ "car_roadster_veryorange",
+ "car_ampera",
+ "car_i3",
+ "car_imiev",
+ "car_ioniq5_cybergray",
+ "car_ioniq_polarwhite",
+ "car_kangoo",
+ "car_kiaev6_white",
+ "car_kianiro_grey",
+ "car_leaf2_pearlwhite",
+ "car_leaf_pearlwhite",
+ "car_mgzs_white",
+ "car_niu_mqi_gt",
+ "car_nrjk",
+ "car_nrjkexperia",
+ "car_smart",
+ "car_thinkcity_classicblack",
+ "car_twizy",
+ "car_vwup_white",
+ "car_zoe_white"
+ )
+
private val availableColors = arrayOf(
"car_roadster_arcticwhite",
"car_roadster_brilliantyellow",
@@ -427,14 +725,14 @@ class CarEditorFragment : BaseFragment() {
)
}
- private class CarImgAdapter : BaseAdapter() {
+ private class CarImgAdapter(private val images: Array, private val isOl: Boolean = false) : BaseAdapter() {
override fun getCount(): Int {
- return availableColors.size
+ return images.size
}
override fun getItem(position: Int): Any {
- return availableColors[position]
+ return images[position]
}
override fun getItemId(position: Int): Long {
@@ -443,9 +741,15 @@ class CarEditorFragment : BaseFragment() {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val iv = convertView as? ImageView ?: ImageView(parent.context)
- iv.setScaleType(ImageView.ScaleType.FIT_START)
+ val density = parent.context.resources.displayMetrics.density
+ iv.layoutParams = Gallery.LayoutParams((200 * density).toInt(), (120 * density).toInt())
+ iv.setScaleType(ImageView.ScaleType.CENTER_INSIDE)
iv.setAdjustViewBounds(true)
- iv.setImageResource(getDrawableIdentifier(parent.context, availableColors[position]))
+ val p = (8 * density).toInt()
+ iv.setPadding(p, p, p, p)
+ iv.setBackgroundResource(R.drawable.gallery_item_selector)
+ val resName = if (isOl) "ol_" + images[position] else images[position]
+ iv.setImageResource(getDrawableIdentifier(parent.context, resName))
return iv
}
}
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/SettingsFragment.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/SettingsFragment.kt
index 8e057f37..eeac3d70 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/pages/settings/SettingsFragment.kt
@@ -13,13 +13,19 @@ import android.widget.AdapterView.OnItemClickListener
import android.widget.BaseAdapter
import android.widget.ImageButton
import android.widget.ImageView
+import android.widget.LinearLayout
import android.widget.ListView
import android.widget.TextView
+import androidx.core.view.MenuHost
+import com.google.android.material.button.MaterialButton
+import androidx.core.view.MenuProvider
+import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import com.openvehicles.OVMS.R
import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.entities.CarData
import com.openvehicles.OVMS.ui.BaseFragment
+import com.openvehicles.OVMS.ui.utils.Ui
import com.openvehicles.OVMS.ui.utils.Ui.getDrawableIdentifier
import com.openvehicles.OVMS.utils.CarsStorage.getStoredCars
@@ -32,33 +38,40 @@ class SettingsFragment : BaseFragment(), OnItemClickListener {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
- listView = ListView(container!!.context)
- listView!!.fitsSystemWindows = true
- return listView
+ val root = inflater.inflate(R.layout.fragment_settings_v2, container, false)
+ listView = root.findViewById(R.id.listView)
+ val appSettingsBtn = root.findViewById(R.id.appSettingsBtn)
+ appSettingsBtn.setOnClickListener { showGlobalOptions() }
+ return root
}
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE)
listView.onItemClickListener = this
listView.setAdapter(SettingsAdapter(activity, getStoredCars()))
- setHasOptionsMenu(true)
- }
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- inflater.inflate(R.menu.settings_options, menu)
- }
+ val menuHost: MenuHost = requireActivity()
+ menuHost.addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.settings_options, menu)
+ }
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == R.id.mi_add) {
- edit(-1)
- return true
- } else if (item.itemId == R.id.mi_globaloptions) {
- showGlobalOptions()
- return true
- }
- return super.onOptionsItemSelected(item)
+ override fun onMenuItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.mi_add -> {
+ edit(-1)
+ true
+ }
+ R.id.mi_globaloptions -> {
+ showGlobalOptions()
+ true
+ }
+ else -> false
+ }
+ }
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun update(carData: CarData?) {
@@ -170,12 +183,17 @@ class SettingsFragment : BaseFragment(), OnItemClickListener {
btnInfo.tag = position
val it = items!![position]
var iv = view.findViewById(R.id.img_car) as ImageView
- iv.setImageResource(
- getDrawableIdentifier(
- parent.context,
- it.sel_vehicle_image
+ val customDrawable = Ui.getCarDrawable(parent.context, it.sel_vehicle_image)
+ if (customDrawable != null) {
+ iv.setImageDrawable(customDrawable)
+ } else {
+ iv.setImageResource(
+ getDrawableIdentifier(
+ parent.context,
+ it.sel_vehicle_image
+ )
)
- )
+ }
(view.findViewById(R.id.txt_title) as TextView).text =
it.sel_vehicle_label
if (listView == null && parent is ListView) {
diff --git a/app/src/main/java/com/openvehicles/OVMS/ui2/rendering/CarRenderingUtils.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/rendering/CarRenderingUtils.kt
index 9a106fff..43b9103a 100644
--- a/app/src/main/java/com/openvehicles/OVMS/ui2/rendering/CarRenderingUtils.kt
+++ b/app/src/main/java/com/openvehicles/OVMS/ui2/rendering/CarRenderingUtils.kt
@@ -17,29 +17,34 @@ object CarRenderingUtils {
fun getTopDownCarLayers(carData: CarData, context: Context, climate: Boolean = false, heat: Boolean = false): List {
var layers = emptyList()
+ val overlayImage = if (carData.sel_vehicle_image_ol.isNotEmpty()) carData.sel_vehicle_image_ol else carData.sel_vehicle_image
+ val customDrawable = Ui.getCarDrawable(context, overlayImage)
+ if (overlayImage.startsWith("file://") && customDrawable != null) {
+ return listOf(customDrawable)
+ }
- var overlayResource: String = carData.sel_vehicle_image
+ var overlayResource: String = overlayImage
- val name_splitted = carData.sel_vehicle_image.split("_");
+ val name_splitted = overlayImage.split("_");
when {
- carData.sel_vehicle_image.startsWith("car_imiev_")
- || carData.sel_vehicle_image.startsWith("car_i3_")
- || carData.sel_vehicle_image.startsWith("car_ampera_")
- || carData.sel_vehicle_image.startsWith("car_twizy_")
- || carData.sel_vehicle_image.startsWith("car_kangoo_") -> {
+ overlayImage.startsWith("car_imiev_")
+ || overlayImage.startsWith("car_i3_")
+ || overlayImage.startsWith("car_ampera_")
+ || overlayImage.startsWith("car_twizy_")
+ || overlayImage.startsWith("car_kangoo_") -> {
// one ol image for all colors:
overlayResource = name_splitted.minus(name_splitted.last())
.joinToString("_")
}
- carData.sel_vehicle_image.startsWith("car_holdenvolt_") -> overlayResource = "car_ampera" // Holdenvolt: one ol image for all colors (same as ampera)
- carData.sel_vehicle_image.startsWith("car_kianiro_") -> overlayResource = "car_kianiro_grey"
- carData.sel_vehicle_image.startsWith("car_smart_44") -> overlayResource = "car_vwup_silver" // Smart forfour share a single image
- carData.sel_vehicle_image.startsWith("car_smart_") -> overlayResource = "car_smart" // Smart fortwo share a single image
- carData.sel_vehicle_image.startsWith("car_nrjkexperia") -> overlayResource = "car_nrjkexperia"
- carData.sel_vehicle_image.startsWith("car_nrjk") -> overlayResource = "car_nrjk"
- carData.sel_vehicle_image.startsWith("car_niu_mqi_gt") -> overlayResource = "car_niu_mqi_gt"
+ overlayImage.startsWith("car_holdenvolt_") -> overlayResource = "car_ampera" // Holdenvolt: one ol image for all colors (same as ampera)
+ overlayImage.startsWith("car_kianiro_") -> overlayResource = "car_kianiro_grey"
+ overlayImage.startsWith("car_smart_44") -> overlayResource = "car_vwup_silver" // Smart forfour share a single image
+ overlayImage.startsWith("car_smart_") -> overlayResource = "car_smart" // Smart fortwo share a single image
+ overlayImage.startsWith("car_nrjkexperia") -> overlayResource = "car_nrjkexperia"
+ overlayImage.startsWith("car_nrjk") -> overlayResource = "car_nrjk"
+ overlayImage.startsWith("car_niu_mqi_gt") -> overlayResource = "car_niu_mqi_gt"
}
var otherResName = overlayResource.split("_").minus(name_splitted.last())
diff --git a/app/src/main/res/color/control_normal.xml b/app/src/main/res/color/control_normal.xml
new file mode 100644
index 00000000..45d234b4
--- /dev/null
+++ b/app/src/main/res/color/control_normal.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_bookmark_add.xml b/app/src/main/res/drawable-anydpi/ic_action_bookmark_add.xml
index 63bfe036..95bcaaa1 100644
--- a/app/src/main/res/drawable-anydpi/ic_action_bookmark_add.xml
+++ b/app/src/main/res/drawable-anydpi/ic_action_bookmark_add.xml
@@ -3,9 +3,9 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="#FFFFFF"
+ android:tint="@color/colorText"
android:alpha="0.8">
diff --git a/app/src/main/res/drawable-anydpi/ic_action_pin.xml b/app/src/main/res/drawable-anydpi/ic_action_pin.xml
index 7c7bb78e..b323b515 100644
--- a/app/src/main/res/drawable-anydpi/ic_action_pin.xml
+++ b/app/src/main/res/drawable-anydpi/ic_action_pin.xml
@@ -3,10 +3,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="#FFFFFF"
+ android:tint="@color/colorText"
android:alpha="0.8">
diff --git a/app/src/main/res/drawable-anydpi/ic_action_send.xml b/app/src/main/res/drawable-anydpi/ic_action_send.xml
index ddc07d00..6528b6d0 100644
--- a/app/src/main/res/drawable-anydpi/ic_action_send.xml
+++ b/app/src/main/res/drawable-anydpi/ic_action_send.xml
@@ -3,10 +3,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="#FFFFFF"
+ android:tint="@color/colorText"
android:alpha="0.8"
android:autoMirrored="true">
diff --git a/app/src/main/res/drawable-anydpi/ic_arrow_forward.xml b/app/src/main/res/drawable-anydpi/ic_arrow_forward.xml
index f5e03a10..62f9c72a 100644
--- a/app/src/main/res/drawable-anydpi/ic_arrow_forward.xml
+++ b/app/src/main/res/drawable-anydpi/ic_arrow_forward.xml
@@ -3,10 +3,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="#FFFFFF"
+ android:tint="@color/colorText"
android:alpha="0.8"
android:autoMirrored="true">
diff --git a/app/src/main/res/drawable-anydpi/ic_bookmarks.xml b/app/src/main/res/drawable-anydpi/ic_bookmarks.xml
index 84792b70..44068b33 100644
--- a/app/src/main/res/drawable-anydpi/ic_bookmarks.xml
+++ b/app/src/main/res/drawable-anydpi/ic_bookmarks.xml
@@ -3,9 +3,9 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="#FFFFFF"
+ android:tint="@color/colorText"
android:alpha="0.8">
diff --git a/app/src/main/res/drawable/gallery_item_selector.xml b/app/src/main/res/drawable/gallery_item_selector.xml
new file mode 100644
index 00000000..4bcc294b
--- /dev/null
+++ b/app/src/main/res/drawable/gallery_item_selector.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_ac.xml b/app/src/main/res/drawable/ic_ac.xml
index e520bcf9..55be1579 100644
--- a/app/src/main/res/drawable/ic_ac.xml
+++ b/app/src/main/res/drawable/ic_ac.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_action_about.xml b/app/src/main/res/drawable/ic_action_about.xml
index b0161393..1d7065c3 100644
--- a/app/src/main/res/drawable/ic_action_about.xml
+++ b/app/src/main/res/drawable/ic_action_about.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_action_control.xml b/app/src/main/res/drawable/ic_action_control.xml
index 5a8f5a8e..c83cf635 100644
--- a/app/src/main/res/drawable/ic_action_control.xml
+++ b/app/src/main/res/drawable/ic_action_control.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_action_discard.xml b/app/src/main/res/drawable/ic_action_discard.xml
index 282594cd..248451f7 100644
--- a/app/src/main/res/drawable/ic_action_discard.xml
+++ b/app/src/main/res/drawable/ic_action_discard.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_action_edit.xml b/app/src/main/res/drawable/ic_action_edit.xml
index 59b6c849..cf9c3a8a 100644
--- a/app/src/main/res/drawable/ic_action_edit.xml
+++ b/app/src/main/res/drawable/ic_action_edit.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_action_new.xml b/app/src/main/res/drawable/ic_action_new.xml
index 37c166cb..3a71e9ba 100644
--- a/app/src/main/res/drawable/ic_action_new.xml
+++ b/app/src/main/res/drawable/ic_action_new.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_action_save.xml b/app/src/main/res/drawable/ic_action_save.xml
index e70f136c..569120e3 100644
--- a/app/src/main/res/drawable/ic_action_save.xml
+++ b/app/src/main/res/drawable/ic_action_save.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_action_settings.xml b/app/src/main/res/drawable/ic_action_settings.xml
index a54c82c2..aa5ecf08 100644
--- a/app/src/main/res/drawable/ic_action_settings.xml
+++ b/app/src/main/res/drawable/ic_action_settings.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_car_sleep.xml b/app/src/main/res/drawable/ic_car_sleep.xml
index 5d77fef8..2b5127f9 100644
--- a/app/src/main/res/drawable/ic_car_sleep.xml
+++ b/app/src/main/res/drawable/ic_car_sleep.xml
@@ -5,5 +5,5 @@
android:viewportHeight="56">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_charger.xml b/app/src/main/res/drawable/ic_charger.xml
index 2dd701f6..e440f4e7 100644
--- a/app/src/main/res/drawable/ic_charger.xml
+++ b/app/src/main/res/drawable/ic_charger.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_chat.xml b/app/src/main/res/drawable/ic_chat.xml
index 829fbb8e..74615ef9 100644
--- a/app/src/main/res/drawable/ic_chat.xml
+++ b/app/src/main/res/drawable/ic_chat.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_climate_schedule.xml b/app/src/main/res/drawable/ic_climate_schedule.xml
index 42eb00fb..925cd11f 100644
--- a/app/src/main/res/drawable/ic_climate_schedule.xml
+++ b/app/src/main/res/drawable/ic_climate_schedule.xml
@@ -5,13 +5,13 @@
android:viewportHeight="24">
diff --git a/app/src/main/res/drawable/ic_controls_tab.xml b/app/src/main/res/drawable/ic_controls_tab.xml
index a9216200..1bd42b13 100644
--- a/app/src/main/res/drawable/ic_controls_tab.xml
+++ b/app/src/main/res/drawable/ic_controls_tab.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_custom_command.xml b/app/src/main/res/drawable/ic_custom_command.xml
index c19864bb..77180557 100644
--- a/app/src/main/res/drawable/ic_custom_command.xml
+++ b/app/src/main/res/drawable/ic_custom_command.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_energy.xml b/app/src/main/res/drawable/ic_energy.xml
index 3a3ef9e9..cf767dd3 100644
--- a/app/src/main/res/drawable/ic_energy.xml
+++ b/app/src/main/res/drawable/ic_energy.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_health.xml b/app/src/main/res/drawable/ic_health.xml
index e0de8731..61ca3bc2 100644
--- a/app/src/main/res/drawable/ic_health.xml
+++ b/app/src/main/res/drawable/ic_health.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml
new file mode 100644
index 00000000..984a5216
--- /dev/null
+++ b/app/src/main/res/drawable/ic_help.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_homelink.xml b/app/src/main/res/drawable/ic_homelink.xml
index 088aea5b..f43891bb 100644
--- a/app/src/main/res/drawable/ic_homelink.xml
+++ b/app/src/main/res/drawable/ic_homelink.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_homelink_num.xml b/app/src/main/res/drawable/ic_homelink_num.xml
index 38f37b2d..f2886c18 100644
--- a/app/src/main/res/drawable/ic_homelink_num.xml
+++ b/app/src/main/res/drawable/ic_homelink_num.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_lock_closed.xml b/app/src/main/res/drawable/ic_lock_closed.xml
index ead35ff0..b2888fc1 100644
--- a/app/src/main/res/drawable/ic_lock_closed.xml
+++ b/app/src/main/res/drawable/ic_lock_closed.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_lock_open.xml b/app/src/main/res/drawable/ic_lock_open.xml
index 17162317..5afca52d 100644
--- a/app/src/main/res/drawable/ic_lock_open.xml
+++ b/app/src/main/res/drawable/ic_lock_open.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_more_vert.xml b/app/src/main/res/drawable/ic_more_vert.xml
new file mode 100644
index 00000000..2eff65ef
--- /dev/null
+++ b/app/src/main/res/drawable/ic_more_vert.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_my_location.xml b/app/src/main/res/drawable/ic_my_location.xml
new file mode 100644
index 00000000..982e003f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_my_location.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_navigation.xml b/app/src/main/res/drawable/ic_navigation.xml
index b4faa861..be72ecd3 100644
--- a/app/src/main/res/drawable/ic_navigation.xml
+++ b/app/src/main/res/drawable/ic_navigation.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_no1.xml b/app/src/main/res/drawable/ic_no1.xml
index 8cdddbd3..5bb07612 100644
--- a/app/src/main/res/drawable/ic_no1.xml
+++ b/app/src/main/res/drawable/ic_no1.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_no2.xml b/app/src/main/res/drawable/ic_no2.xml
index 26945b55..7e976e4c 100644
--- a/app/src/main/res/drawable/ic_no2.xml
+++ b/app/src/main/res/drawable/ic_no2.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_no3.xml b/app/src/main/res/drawable/ic_no3.xml
index 66e049c0..2bb7cfa9 100644
--- a/app/src/main/res/drawable/ic_no3.xml
+++ b/app/src/main/res/drawable/ic_no3.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_notification_icon.xml b/app/src/main/res/drawable/ic_notification_icon.xml
index 2d74f18a..2c085dd7 100644
--- a/app/src/main/res/drawable/ic_notification_icon.xml
+++ b/app/src/main/res/drawable/ic_notification_icon.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_odometer.xml b/app/src/main/res/drawable/ic_odometer.xml
new file mode 100644
index 00000000..58aa38e8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_odometer.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml
new file mode 100644
index 00000000..66baeba0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_refresh.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_remote_control.xml b/app/src/main/res/drawable/ic_remote_control.xml
index 21ff68da..f1958e2a 100644
--- a/app/src/main/res/drawable/ic_remote_control.xml
+++ b/app/src/main/res/drawable/ic_remote_control.xml
@@ -27,7 +27,7 @@
android:pathData="m67.11,53.91 l-27.96,0.02c0,-0 -6.4,-7.44 -6.47,-7.89l41.48,-0.03zM33.28,92.9 L32.74,63.43 39.38,63.43 39.89,86.06 66.37,86.04 67.13,63.41 73.76,63.4 73.03,92.87 33.28,92.9"
android:strokeAlpha="0.85045"
android:strokeWidth="0.72"
- android:fillColor="#ffffff"
+ android:fillColor="@color/colorText"
android:strokeColor="#00000000"
android:fillType="nonZero"
android:fillAlpha="0.85045"/>
diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml
index f995c5c6..96a0a7a0 100644
--- a/app/src/main/res/drawable/ic_satellite.xml
+++ b/app/src/main/res/drawable/ic_satellite.xml
@@ -4,6 +4,6 @@
android:viewportWidth="591.49"
android:viewportHeight="591.59">
diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml
index a54c82c2..aa5ecf08 100644
--- a/app/src/main/res/drawable/ic_settings.xml
+++ b/app/src/main/res/drawable/ic_settings.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/ic_tabs_menu.xml b/app/src/main/res/drawable/ic_tabs_menu.xml
index d34a5c0e..afdee1f4 100644
--- a/app/src/main/res/drawable/ic_tabs_menu.xml
+++ b/app/src/main/res/drawable/ic_tabs_menu.xml
@@ -6,6 +6,6 @@
android:viewportHeight="24">
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_temp.xml b/app/src/main/res/drawable/ic_temp.xml
index 54da5a27..cce01cfb 100644
--- a/app/src/main/res/drawable/ic_temp.xml
+++ b/app/src/main/res/drawable/ic_temp.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/ic_zoom_in.xml b/app/src/main/res/drawable/ic_zoom_in.xml
new file mode 100644
index 00000000..671efd60
--- /dev/null
+++ b/app/src/main/res/drawable/ic_zoom_in.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/signal_strength_0.xml b/app/src/main/res/drawable/signal_strength_0.xml
index b3c9b72c..d5c7490d 100644
--- a/app/src/main/res/drawable/signal_strength_0.xml
+++ b/app/src/main/res/drawable/signal_strength_0.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/signal_strength_1.xml b/app/src/main/res/drawable/signal_strength_1.xml
index 70febf4c..90e83a51 100644
--- a/app/src/main/res/drawable/signal_strength_1.xml
+++ b/app/src/main/res/drawable/signal_strength_1.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/signal_strength_2.xml b/app/src/main/res/drawable/signal_strength_2.xml
index 423f201d..2f8e88e7 100644
--- a/app/src/main/res/drawable/signal_strength_2.xml
+++ b/app/src/main/res/drawable/signal_strength_2.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/signal_strength_3.xml b/app/src/main/res/drawable/signal_strength_3.xml
index f17f9c48..62cefe7a 100644
--- a/app/src/main/res/drawable/signal_strength_3.xml
+++ b/app/src/main/res/drawable/signal_strength_3.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/signal_strength_4.xml b/app/src/main/res/drawable/signal_strength_4.xml
index ee8471f8..cfef59b9 100644
--- a/app/src/main/res/drawable/signal_strength_4.xml
+++ b/app/src/main/res/drawable/signal_strength_4.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/signal_strength_5.xml b/app/src/main/res/drawable/signal_strength_5.xml
index a44d73fa..6654cdcc 100644
--- a/app/src/main/res/drawable/signal_strength_5.xml
+++ b/app/src/main/res/drawable/signal_strength_5.xml
@@ -5,5 +5,5 @@
android:viewportHeight="960">
+ android:fillColor="@color/colorText"/>
diff --git a/app/src/main/res/drawable/topview_ac_arrows.xml b/app/src/main/res/drawable/topview_ac_arrows.xml
index f9598dc7..565a7ce0 100644
--- a/app/src/main/res/drawable/topview_ac_arrows.xml
+++ b/app/src/main/res/drawable/topview_ac_arrows.xml
@@ -10,7 +10,7 @@
+ android:fillColor="@color/colorText" />
+ android:fillColor="@color/colorText" />
+ android:fillColor="@color/colorText" />
diff --git a/app/src/main/res/layout/activity_main2.xml b/app/src/main/res/layout/activity_main2.xml
index f19fbd80..6066a50d 100644
--- a/app/src/main/res/layout/activity_main2.xml
+++ b/app/src/main/res/layout/activity_main2.xml
@@ -15,27 +15,25 @@
+ android:layout_height="?attr/actionBarSize">
-
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/charging_card.xml b/app/src/main/res/layout/charging_card.xml
index e6afe12b..b85b51ef 100644
--- a/app/src/main/res/layout/charging_card.xml
+++ b/app/src/main/res/layout/charging_card.xml
@@ -7,15 +7,28 @@
android:layout_margin="3dp"
android:padding="8dp">
+
+
-
+
@@ -42,7 +42,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Information"
- android:textColor="@android:color/white"
+ android:textColor="@color/colorText"
android:textSize="20sp"
android:textStyle="bold" />
diff --git a/app/src/main/res/layout/dialog_edit_tpms_config.xml b/app/src/main/res/layout/dialog_edit_tpms_config.xml
new file mode 100644
index 00000000..0aad630c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_edit_tpms_config.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dlg_stored_command.xml b/app/src/main/res/layout/dlg_stored_command.xml
index 8fbdbeb5..1d2af36a 100755
--- a/app/src/main/res/layout/dlg_stored_command.xml
+++ b/app/src/main/res/layout/dlg_stored_command.xml
@@ -15,6 +15,8 @@
android:autofillHints=""
android:hint="@string/lb_enter_title"
android:inputType="text"
+ android:textColor="@color/colorText"
+ android:textColorHint="@color/colorGreyBlue"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" />
@@ -26,9 +28,12 @@
android:fontFamily="monospace"
android:hint="@string/lb_enter_command"
android:inputType="text|textNoSuggestions"
+ android:textColor="@color/colorText"
+ android:textColorHint="@color/colorGreyBlue"
android:maxLines="1"
android:minLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:typeface="monospace" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/energy_metric.xml b/app/src/main/res/layout/energy_metric.xml
index b6146a97..b8445303 100644
--- a/app/src/main/res/layout/energy_metric.xml
+++ b/app/src/main/res/layout/energy_metric.xml
@@ -1,33 +1,29 @@
-
+ android:padding="12dp">
-
+
-
+
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_car.xml b/app/src/main/res/layout/fragment_car.xml
index 6a661524..08650a36 100644
--- a/app/src/main/res/layout/fragment_car.xml
+++ b/app/src/main/res/layout/fragment_car.xml
@@ -26,37 +26,37 @@
android:id="@+id/tabCarImageCarTrunkOpen"
style="@style/TabCarImgCar"
android:src="@drawable/roadster_outline_tr"
- android:visibility="invisible" />
+ android:visibility="gone" />
+ android:visibility="gone" />
+ android:visibility="gone" />
+ android:visibility="gone" />
+ android:visibility="gone" />
+ android:visibility="gone" />
+ android:visibility="gone" />
@@ -306,7 +306,7 @@
android:gravity="center"
android:singleLine="false"
android:text="▴999.9 ▾99.9 kWh"
- android:textColor="@color/colorForeground"
+ android:textColor="@color/colorText"
android:textSize="22px"
android:typeface="monospace" />
@@ -321,7 +321,7 @@
android:gravity="center"
android:singleLine="false"
android:text="➟1234.5km"
- android:textColor="@color/colorForeground"
+ android:textColor="@color/colorText"
android:textSize="22px"
android:typeface="monospace" />
@@ -338,7 +338,7 @@
android:phoneNumber="true"
android:singleLine="false"
android:text="123.4kph"
- android:textColor="@color/colorForeground"
+ android:textColor="@color/colorText"
android:textSize="32px"
android:textStyle="bold"
android:typeface="monospace" />
diff --git a/app/src/main/res/layout/fragment_careditor_v2.xml b/app/src/main/res/layout/fragment_careditor_v2.xml
index 5a3a8b4d..de5e80f9 100755
--- a/app/src/main/res/layout/fragment_careditor_v2.xml
+++ b/app/src/main/res/layout/fragment_careditor_v2.xml
@@ -144,18 +144,162 @@
android:layout_marginStart="20dp"
android:text="@string/lb_tls_trust_all" />
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_charging.xml b/app/src/main/res/layout/fragment_charging.xml
index 64af9c68..552623fb 100644
--- a/app/src/main/res/layout/fragment_charging.xml
+++ b/app/src/main/res/layout/fragment_charging.xml
@@ -11,7 +11,7 @@
android:id="@+id/chargingProgressBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:visibility="invisible"
+ android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:elevation="3dp"
android:indeterminate="true"
- android:visibility="invisible"
+ android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -64,6 +64,7 @@
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
+ android:textColor="@color/colorText"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="@+id/battSoc"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
@@ -77,6 +78,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="center"
+ android:textColor="@color/colorText"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
app:layout_constraintTop_toBottomOf="@+id/battIndicatorImg" />
@@ -88,6 +90,7 @@
android:layout_marginStart="16dp"
android:gravity="center"
android:textSize="24sp"
+ android:textColor="@color/colorText"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
@@ -99,6 +102,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battVolt"
app:layout_constraintEnd_toStartOf="@+id/battVolt"
app:layout_constraintHorizontal_bias="0.0"
@@ -112,6 +116,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battTemp"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
app:layout_constraintTop_toTopOf="@+id/battTemp"
@@ -123,6 +128,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintEnd_toStartOf="@+id/battAmp"
app:layout_constraintTop_toTopOf="@+id/battAmp"
tools:text="370V" />
@@ -133,6 +139,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battIndicatorImg"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
@@ -144,6 +151,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battVolt"
app:layout_constraintStart_toEndOf="@+id/battAmp"
app:layout_constraintTop_toTopOf="@+id/battVolt"
@@ -192,6 +200,10 @@
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:contentPadding="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -266,6 +278,10 @@
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:contentPadding="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -291,6 +307,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
+ android:textColor="@color/colorText"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -359,6 +376,10 @@
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chargeModeCard">
@@ -415,6 +436,10 @@
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/socLimitCard">
@@ -472,6 +497,10 @@
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="32dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:contentPadding="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/fragment_controls.xml b/app/src/main/res/layout/fragment_controls.xml
index 9f887bbc..dd7154d9 100644
--- a/app/src/main/res/layout/fragment_controls.xml
+++ b/app/src/main/res/layout/fragment_controls.xml
@@ -1,175 +1,317 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:paddingBottom="32dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_energy.xml b/app/src/main/res/layout/fragment_energy.xml
index 31dcf1f6..4ab9937f 100644
--- a/app/src/main/res/layout/fragment_energy.xml
+++ b/app/src/main/res/layout/fragment_energy.xml
@@ -33,6 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/battery_data_soh"
+ android:textColor="@color/colorText"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/battSoh"
app:layout_constraintEnd_toEndOf="@+id/battSoh"
@@ -44,6 +45,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/battery_data_soc"
+ android:textColor="@color/colorText"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/battSoc"
app:layout_constraintEnd_toEndOf="@+id/battSoc"
@@ -56,6 +58,7 @@
android:layout_marginStart="16dp"
android:gravity="center"
android:textSize="24sp"
+ android:textColor="@color/colorText"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
@@ -69,6 +72,7 @@
android:layout_marginEnd="16dp"
android:gravity="center"
android:textSize="24sp"
+ android:textColor="@color/colorText"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/battIndicatorImg"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
@@ -80,6 +84,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toTopOf="@+id/battSoc"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
@@ -92,6 +97,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintEnd_toStartOf="@+id/battAmp"
app:layout_constraintTop_toTopOf="@+id/battAmp"
tools:text="370V" />
@@ -102,6 +108,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battIndicatorImg"
app:layout_constraintEnd_toEndOf="@+id/battIndicatorImg"
app:layout_constraintStart_toStartOf="@+id/battIndicatorImg"
@@ -113,6 +120,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:textSize="16sp"
+ android:textColor="@color/colorText"
app:layout_constraintBottom_toBottomOf="@+id/battVolt"
app:layout_constraintStart_toEndOf="@+id/battAmp"
app:layout_constraintTop_toTopOf="@+id/battVolt"
@@ -134,11 +142,21 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
+ android:layout_marginTop="12dp"
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer">
+
+
+
-
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ >
-
+ android:paddingTop="16dp">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
+ android:layout_height="180dp"
+ android:layout_marginTop="8dp"
+ app:layout_constraintTop_toBottomOf="@+id/odoContainer">
-
-
-
+
+
+
+
+
+
+
+
+
+ android:layout_marginTop="-24dp"
+ android:orientation="vertical">
-
-
-
+
+
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:padding="12dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
+
+
+
+
+
+
+
+
+
-
-
-
+
-
+
-
-
-
-
-
-
+
-
+
+
+
+
+
+
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_settings_v2.xml b/app/src/main/res/layout/fragment_settings_v2.xml
new file mode 100644
index 00000000..4dc73749
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings_v2.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_stored_commands.xml b/app/src/main/res/layout/fragment_stored_commands.xml
new file mode 100644
index 00000000..7725424f
--- /dev/null
+++ b/app/src/main/res/layout/fragment_stored_commands.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/home_tab_item.xml b/app/src/main/res/layout/home_tab_item.xml
index 6a815dad..ff81b306 100644
--- a/app/src/main/res/layout/home_tab_item.xml
+++ b/app/src/main/res/layout/home_tab_item.xml
@@ -3,12 +3,14 @@
+ app:cardCornerRadius="16dp"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/colorGreyBlue"
+ app:cardBackgroundColor="?attr/colorSurfaceContainer">
@@ -82,7 +84,7 @@
app:layout_constraintEnd_toStartOf="@+id/hideIndicator"
app:layout_constraintTop_toTopOf="@+id/linearLayout"
app:srcCompat="@drawable/ic_tab_chevron"
- app:tint="?attr/colorOnSecondaryContainer" />
+ app:tint="?attr/colorOnSurface" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_car.xml b/app/src/main/res/layout/item_car.xml
index 4200cb29..0dc569e6 100755
--- a/app/src/main/res/layout/item_car.xml
+++ b/app/src/main/res/layout/item_car.xml
@@ -1,5 +1,6 @@
@@ -36,19 +37,19 @@
diff --git a/app/src/main/res/layout/item_car_v2.xml b/app/src/main/res/layout/item_car_v2.xml
index f19132c9..79cf10f0 100755
--- a/app/src/main/res/layout/item_car_v2.xml
+++ b/app/src/main/res/layout/item_car_v2.xml
@@ -62,7 +62,7 @@
android:background="@drawable/bg_img_button"
android:padding="8dp"
android:scaleType="centerCrop"
- android:src="@drawable/ic_action_edit" />
+ app:srcCompat="@drawable/ic_action_edit" />
+ app:srcCompat="@drawable/ic_action_control" />
+ app:srcCompat="@drawable/ic_action_about" />
diff --git a/app/src/main/res/layout/item_notifications_received.xml b/app/src/main/res/layout/item_notifications_received.xml
index f2842adc..30ed195b 100755
--- a/app/src/main/res/layout/item_notifications_received.xml
+++ b/app/src/main/res/layout/item_notifications_received.xml
@@ -27,7 +27,7 @@
android:paddingBottom="2dp"
android:text="Time"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/abc_secondary_text_material_dark"
+ android:textColor="?android:attr/textColorSecondary"
android:textSize="11sp" />
diff --git a/app/src/main/res/layout/item_notifications_sent.xml b/app/src/main/res/layout/item_notifications_sent.xml
index 0853fd8a..564a538d 100755
--- a/app/src/main/res/layout/item_notifications_sent.xml
+++ b/app/src/main/res/layout/item_notifications_sent.xml
@@ -28,7 +28,7 @@
android:paddingBottom="2dp"
android:text="Time"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/abc_secondary_text_material_dark"
+ android:textColor="?android:attr/textColorSecondary"
android:textSize="11sp" />
diff --git a/app/src/main/res/layout/item_notifications_v2.xml b/app/src/main/res/layout/item_notifications_v2.xml
index 5b874803..c391c084 100755
--- a/app/src/main/res/layout/item_notifications_v2.xml
+++ b/app/src/main/res/layout/item_notifications_v2.xml
@@ -22,6 +22,7 @@
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="6dp"
+ app:tint="@color/colorText"
app:srcCompat="@drawable/ic_action_about" />
@@ -59,7 +60,7 @@
android:autoLink="none"
android:text="Message"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/abc_primary_text_material_dark"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="12sp" />
diff --git a/app/src/main/res/layout/item_stored_command.xml b/app/src/main/res/layout/item_stored_command.xml
index 52e13e73..d44ec55f 100755
--- a/app/src/main/res/layout/item_stored_command.xml
+++ b/app/src/main/res/layout/item_stored_command.xml
@@ -1,5 +1,6 @@
@@ -57,7 +60,7 @@
android:contentDescription="@string/Delete"
android:focusable="true"
android:padding="16dp"
- android:src="@drawable/ic_action_discard" />
+ app:srcCompat="@drawable/ic_action_discard" />
+ app:srcCompat="@drawable/ic_action_pin" />
+ app:srcCompat="@drawable/ic_action_send" />
diff --git a/app/src/main/res/layout/material3_preference_switch_layout.xml b/app/src/main/res/layout/material3_preference_switch_layout.xml
index bb35e1f6..74a556d4 100644
--- a/app/src/main/res/layout/material3_preference_switch_layout.xml
+++ b/app/src/main/res/layout/material3_preference_switch_layout.xml
@@ -6,4 +6,5 @@
android:layout_marginRight="5dp"
android:background="@null"
android:clickable="false"
- android:focusable="false" />
\ No newline at end of file
+ android:focusable="false"
+ android:visibility="visible"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/quick_action_item.xml b/app/src/main/res/layout/quick_action_item.xml
index 3278eb8d..d2f3fb3e 100644
--- a/app/src/main/res/layout/quick_action_item.xml
+++ b/app/src/main/res/layout/quick_action_item.xml
@@ -11,9 +11,8 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="3dp"
- android:elevation="10dp"
android:indeterminate="true"
- android:visibility="invisible"
+ android:visibility="gone"
app:indicatorSize="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -22,24 +21,26 @@
tools:visibility="visible" />
-
-
-
-
+ tools:icon="@drawable/ic_ac" />
\ No newline at end of file
diff --git a/app/src/main/res/menu/auxbattery_chart_options.xml b/app/src/main/res/menu/auxbattery_chart_options.xml
index 5c03ca54..f1325e4c 100644
--- a/app/src/main/res/menu/auxbattery_chart_options.xml
+++ b/app/src/main/res/menu/auxbattery_chart_options.xml
@@ -4,12 +4,12 @@
-
@@ -17,7 +17,7 @@
@@ -37,7 +37,7 @@
diff --git a/app/src/main/res/menu/battery_chart_options.xml b/app/src/main/res/menu/battery_chart_options.xml
index 5c03ca54..f1325e4c 100644
--- a/app/src/main/res/menu/battery_chart_options.xml
+++ b/app/src/main/res/menu/battery_chart_options.xml
@@ -4,12 +4,12 @@
-
@@ -17,7 +17,7 @@
@@ -37,7 +37,7 @@
diff --git a/app/src/main/res/menu/battery_options.xml b/app/src/main/res/menu/battery_options.xml
index 04332aaf..1d6b9a15 100644
--- a/app/src/main/res/menu/battery_options.xml
+++ b/app/src/main/res/menu/battery_options.xml
@@ -9,7 +9,7 @@
-
@@ -51,7 +51,7 @@
diff --git a/app/src/main/res/menu/car_options.xml b/app/src/main/res/menu/car_options.xml
index da0deac7..f8f25864 100644
--- a/app/src/main/res/menu/car_options.xml
+++ b/app/src/main/res/menu/car_options.xml
@@ -9,7 +9,7 @@
-
diff --git a/app/src/main/res/menu/map_options.xml b/app/src/main/res/menu/map_options.xml
index c186ce18..faffb6a0 100644
--- a/app/src/main/res/menu/map_options.xml
+++ b/app/src/main/res/menu/map_options.xml
@@ -4,7 +4,7 @@
-