From d70e7cf26788c94ca6ec5a01cc9e78bbf6fe67e4 Mon Sep 17 00:00:00 2001
From: zorgms <100448801+zorgms@users.noreply.github.com>
Date: Tue, 4 Aug 2026 21:12:56 +0200
Subject: [PATCH 1/5] Updated UI2 to day/night/follow system
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The update was created with the help of AI Gemini 3 Flash Preview.
The entire UI2 design has been overhauled and updated to include a day/night theme.
Switching between the new and old UI and changing the theme takes place without restarting the app.
On the control side, the TPMS XSQ Config target values can be set for the smart EQ.
---
app/build.gradle | 1 +
app/src/main/AndroidManifest.xml | 2 +
.../java/com/openvehicles/OVMS/BaseApp.kt | 3 +
.../com/openvehicles/OVMS/ui/ApiActivity.kt | 6 +
.../OVMS/ui/AuxBatteryFragment.kt | 51 +-
.../OVMS/ui/BaseFragmentActivity.kt | 3 +
.../openvehicles/OVMS/ui/BatteryFragment.kt | 81 ++-
.../com/openvehicles/OVMS/ui/MainActivity.kt | 26 +-
.../com/openvehicles/OVMS/ui/PowerFragment.kt | 71 ++-
.../OVMS/ui/settings/GlobalOptionsFragment.kt | 7 +
.../OVMS/ui/settings/StoredCommandFragment.kt | 25 +-
.../openvehicles/OVMS/ui2/MainActivityUI2.kt | 61 +-
.../energymetrics/EnergyMetricsAdapter.kt | 22 +-
.../components/hometabs/HomeTabsAdapter.kt | 4 +-
.../quickactions/CarInfoQuickAction.kt | 7 +-
.../quickactions/ChargingQuickAction.kt | 6 +-
.../quickactions/ClimateQuickAction.kt | 4 +-
.../ClimateScheduleQuickAction.kt | 4 +-
.../quickactions/DDT4allQuickAction.kt | 7 +-
.../quickactions/HomelinkQuickAction.kt | 7 +-
.../quickactions/LockQuickAction.kt | 7 +-
.../components/quickactions/QuickAction.kt | 61 +-
.../quickactions/ValetQuickAction.kt | 7 +-
.../quickactions/WakeupQuickAction.kt | 8 +-
.../adapters/QuickActionsEditorAdapter.kt | 6 +-
.../openvehicles/OVMS/ui2/misc/ThemeMode.kt | 57 ++
.../OVMS/ui2/pages/ChargingFragment.kt | 5 +-
.../OVMS/ui2/pages/ClimateFragment.kt | 10 +-
.../OVMS/ui2/pages/ControlsFragment.kt | 271 +++++++--
.../OVMS/ui2/pages/EnergyFragment.kt | 9 +-
.../OVMS/ui2/pages/HomeFragment.kt | 192 ++++--
.../pages/settings/AppUISettingsFragment.kt | 24 +-
.../ui2/pages/settings/CarEditorFragment.kt | 55 +-
.../ui2/pages/settings/SettingsFragment.kt | 50 +-
app/src/main/res/color/control_normal.xml | 4 +
.../ic_action_bookmark_add.xml | 4 +-
.../res/drawable-anydpi/ic_action_pin.xml | 4 +-
.../res/drawable-anydpi/ic_action_send.xml | 4 +-
.../res/drawable-anydpi/ic_arrow_forward.xml | 4 +-
.../main/res/drawable-anydpi/ic_bookmarks.xml | 4 +-
app/src/main/res/drawable/ic_ac.xml | 2 +-
app/src/main/res/drawable/ic_action_about.xml | 4 +-
.../main/res/drawable/ic_action_control.xml | 6 +-
.../main/res/drawable/ic_action_discard.xml | 4 +-
app/src/main/res/drawable/ic_action_edit.xml | 4 +-
app/src/main/res/drawable/ic_action_new.xml | 4 +-
app/src/main/res/drawable/ic_action_save.xml | 4 +-
.../main/res/drawable/ic_action_settings.xml | 4 +-
app/src/main/res/drawable/ic_car_sleep.xml | 2 +-
app/src/main/res/drawable/ic_charger.xml | 2 +-
app/src/main/res/drawable/ic_chat.xml | 2 +-
.../main/res/drawable/ic_climate_schedule.xml | 6 +-
app/src/main/res/drawable/ic_controls_tab.xml | 4 +-
.../main/res/drawable/ic_custom_command.xml | 4 +-
app/src/main/res/drawable/ic_energy.xml | 4 +-
app/src/main/res/drawable/ic_health.xml | 2 +-
app/src/main/res/drawable/ic_help.xml | 10 +
app/src/main/res/drawable/ic_homelink.xml | 2 +-
app/src/main/res/drawable/ic_homelink_num.xml | 2 +-
app/src/main/res/drawable/ic_lock_closed.xml | 2 +-
app/src/main/res/drawable/ic_lock_open.xml | 2 +-
app/src/main/res/drawable/ic_more_vert.xml | 10 +
app/src/main/res/drawable/ic_my_location.xml | 10 +
app/src/main/res/drawable/ic_navigation.xml | 4 +-
app/src/main/res/drawable/ic_no1.xml | 4 +-
app/src/main/res/drawable/ic_no2.xml | 4 +-
app/src/main/res/drawable/ic_no3.xml | 4 +-
.../res/drawable/ic_notification_icon.xml | 2 +-
app/src/main/res/drawable/ic_odometer.xml | 9 +
app/src/main/res/drawable/ic_refresh.xml | 10 +
.../main/res/drawable/ic_remote_control.xml | 2 +-
app/src/main/res/drawable/ic_satellite.xml | 2 +-
app/src/main/res/drawable/ic_settings.xml | 4 +-
app/src/main/res/drawable/ic_tabs_menu.xml | 2 +-
app/src/main/res/drawable/ic_temp.xml | 2 +-
app/src/main/res/drawable/ic_zoom_in.xml | 10 +
.../main/res/drawable/signal_strength_0.xml | 2 +-
.../main/res/drawable/signal_strength_1.xml | 2 +-
.../main/res/drawable/signal_strength_2.xml | 2 +-
.../main/res/drawable/signal_strength_3.xml | 2 +-
.../main/res/drawable/signal_strength_4.xml | 2 +-
.../main/res/drawable/signal_strength_5.xml | 2 +-
.../main/res/drawable/topview_ac_arrows.xml | 6 +-
app/src/main/res/layout/activity_main2.xml | 12 +-
app/src/main/res/layout/charging_card.xml | 15 +-
app/src/main/res/layout/detail.xml | 4 +-
.../res/layout/dialog_edit_tpms_config.xml | 82 +++
.../main/res/layout/dlg_stored_command.xml | 5 +
app/src/main/res/layout/energy_metric.xml | 48 +-
app/src/main/res/layout/fragment_car.xml | 22 +-
app/src/main/res/layout/fragment_charging.xml | 33 +-
app/src/main/res/layout/fragment_controls.xml | 448 ++++++++------
app/src/main/res/layout/fragment_energy.xml | 24 +-
app/src/main/res/layout/fragment_home.xml | 566 ++++++++++--------
.../main/res/layout/fragment_settings_v2.xml | 24 +
.../res/layout/fragment_stored_commands.xml | 15 +
app/src/main/res/layout/home_tab_item.xml | 14 +-
app/src/main/res/layout/item_car.xml | 7 +-
app/src/main/res/layout/item_car_v2.xml | 6 +-
.../layout/item_notifications_received.xml | 4 +-
.../res/layout/item_notifications_sent.xml | 4 +-
.../main/res/layout/item_notifications_v2.xml | 7 +-
.../main/res/layout/item_stored_command.xml | 23 +-
.../material3_preference_switch_layout.xml | 3 +-
app/src/main/res/layout/quick_action_item.xml | 29 +-
.../res/menu/auxbattery_chart_options.xml | 8 +-
.../main/res/menu/battery_chart_options.xml | 8 +-
app/src/main/res/menu/battery_options.xml | 4 +-
app/src/main/res/menu/car_options.xml | 2 +-
app/src/main/res/menu/map_options.xml | 2 +-
.../main/res/menu/notifications_options.xml | 4 +-
app/src/main/res/menu/power_chart_options.xml | 12 +-
app/src/main/res/values-de/strings.xml | 7 +
app/src/main/res/values-night/colors.xml | 6 +
app/src/main/res/values-v21/style.xml | 48 +-
app/src/main/res/values/arrays.xml | 14 +-
app/src/main/res/values/colors.xml | 13 +-
app/src/main/res/values/dimens.xml | 5 +-
app/src/main/res/values/strings.xml | 7 +
app/src/main/res/values/style.xml | 61 +-
app/src/main/res/xml/app_preferences.xml | 16 +
121 files changed, 2050 insertions(+), 919 deletions(-)
create mode 100644 app/src/main/java/com/openvehicles/OVMS/ui2/misc/ThemeMode.kt
create mode 100644 app/src/main/res/color/control_normal.xml
create mode 100644 app/src/main/res/drawable/ic_help.xml
create mode 100644 app/src/main/res/drawable/ic_more_vert.xml
create mode 100644 app/src/main/res/drawable/ic_my_location.xml
create mode 100644 app/src/main/res/drawable/ic_odometer.xml
create mode 100644 app/src/main/res/drawable/ic_refresh.xml
create mode 100644 app/src/main/res/drawable/ic_zoom_in.xml
create mode 100644 app/src/main/res/layout/dialog_edit_tpms_config.xml
create mode 100644 app/src/main/res/layout/fragment_settings_v2.xml
create mode 100644 app/src/main/res/layout/fragment_stored_commands.xml
create mode 100644 app/src/main/res/values-night/colors.xml
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 @@
{
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..b6825c7e 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,8 +71,7 @@ class StoredCommandFragment : BaseFragment(), OnItemClickListener {
// Create user interface:
activity?.setTitle(R.string.stored_commands_title)
setHasOptionsMenu(true)
- listView = ListView(container.context)
- listView.fitsSystemWindows = true
+ listView = ListView(context)
listView.onItemClickListener = this
adapter = StoredCommandAdapter(context, R.layout.item_stored_command, storedCommands, inflater)
listView.setAdapter(adapter)
@@ -118,7 +113,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 +127,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 +148,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 +261,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/ui2/MainActivityUI2.kt b/app/src/main/java/com/openvehicles/OVMS/ui2/MainActivityUI2.kt
index 228349bb..bf7e9552 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
@@ -37,6 +45,7 @@ import com.openvehicles.OVMS.api.ApiService
import com.openvehicles.OVMS.ui.ApiActivity
import com.openvehicles.OVMS.ui.MainActivity
import com.openvehicles.OVMS.ui.MapFragment
+import com.openvehicles.OVMS.ui2.misc.ThemeMode
import com.openvehicles.OVMS.utils.AppPrefs
import com.openvehicles.OVMS.utils.Sys.getRandomString
import java.util.UUID
@@ -50,6 +59,7 @@ class MainActivityUI2 : ApiActivity() {
}
private lateinit var navController: NavController
+
/**
* ApiService / OVMS server communication:
*
@@ -78,12 +88,47 @@ 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") {
+
+ 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 +204,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 +225,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)
@@ -492,8 +539,12 @@ class MainActivityUI2 : ApiActivity() {
override fun onDestroy() {
Log.d(TAG, "onDestroy")
- if (apiEventReceiver != null)
- unregisterReceiver(apiEventReceiver)
+ 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..0dbf393c 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,198 @@ class ControlsFragment : BaseFragment(), OnResultCommandListener {
initialiseSideActions(carData)
initialiseMainActions(carData)
initialiseCarRendering(carData)
+ loadTPMSConfig()
+
+ findViewById(R.id.btnEditTPMSConfig).setOnClickListener {
+ showEditTPMSConfigDialog()
+ }
+ }
+
+ 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)
+
+ 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
+
+ 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 +317,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 +334,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 +371,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 +581,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 +603,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 +629,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 +641,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 +705,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..f8fff4ab 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) }
@@ -541,6 +551,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
}
@@ -580,8 +593,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 +629,82 @@ 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
+ if (socState > 1)
+ 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
+
+ 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)
+ }
+ else -> ""
+ }
+
+ rangeUnitText.text = when (socState) {
+ 0 -> unit
+ 1 -> "kWh/100$unit"
+ 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 +721,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)
@@ -805,28 +865,24 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
)
)!!)
- val speedShownInUI = car_tire_image1 > 0 && (carData?.car_started == true||CAR_RENDER_TEST_MODE_D)
+ 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 animationDrawable = AppCompatResources.getDrawable(requireContext(), car_tire_image1) as AnimationDrawable
val carAnim = CarAnimationDrawable()
var drawables = emptyList()
- for (i in 0.. 0) {
- // Adjust animaion speed
+ // Adjust animation speed
carAnim.start()
- carAnim.setDuration((320 / (carData?.car_speed_raw!!/3.5)).toInt())
+ carAnim.setDuration((320 / (carData?.car_speed_raw!! / 3.5)).toInt())
}
-
}
if (carData?.car_headlights_on == true || CAR_RENDER_TEST_MODE_HD) {
@@ -928,6 +984,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 +993,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
if (modeResource > 0)
layers = layers.plus(ContextCompat.getDrawable(requireContext(), modeResource)!!)
}
+ */
val newDrawable = LayerDrawable(layers.toTypedArray())
@@ -949,39 +1007,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 +1086,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) {
@@ -1714,6 +1792,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
"signal_strength_" + carData?.car_gsm_bars
)
)
+ gsmimg.imageTintList = ColorStateList.valueOf(textColor)
}
}
@@ -1722,6 +1801,7 @@ class HomeFragment : BaseFragment(), OnResultCommandListener, HomeTabsAdapter.It
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.imageTintList = ColorStateList.valueOf(textColor)
}
}
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..4132e7ae 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
@@ -57,8 +60,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) {
@@ -91,33 +94,35 @@ class CarEditorFragment : BaseFragment() {
AdapterView.OnItemClickListener { parent, view, position, id -> setSelectedServer(position, true) }
galleryCar = requireView().findViewById(R.id.ga_car) as Gallery
galleryCar!!.setAdapter(CarImgAdapter())
- setHasOptionsMenu(true)
- load()
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- inflater.inflate(R.menu.control_save_delete, menu)
- }
- 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)
- }
+ val menuHost: MenuHost = requireActivity()
+ menuHost.addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.control_save_delete, menu)
+ }
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.mi_save -> {
- save()
- 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)
}
- R.id.mi_delete -> {
- delete()
- true
+
+ override fun onMenuItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.mi_save -> {
+ save()
+ true
+ }
+ R.id.mi_delete -> {
+ delete()
+ true
+ }
+ else -> false
+ }
}
- else -> false
- }
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
+
+ load()
}
private fun delete() {
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..45a4acce 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,8 +13,13 @@ 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
@@ -32,33 +37,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?) {
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/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..aea69b2e 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="#FFFFFF"
+ android:textColorHint="#AAAAAA"
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="#FFFFFF"
+ android:textColorHint="#AAAAAA"
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_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..f7077b46 100644
--- a/app/src/main/res/layout/fragment_controls.xml
+++ b/app/src/main/res/layout/fragment_controls.xml
@@ -1,175 +1,289 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ 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: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..b69d84b2
--- /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..68c449d2 100755
--- a/app/src/main/res/layout/item_stored_command.xml
+++ b/app/src/main/res/layout/item_stored_command.xml
@@ -1,8 +1,10 @@
@@ -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..24e828c2 100644
--- a/app/src/main/res/menu/map_options.xml
+++ b/app/src/main/res/menu/map_options.xml
@@ -4,7 +4,7 @@
-