diff --git a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/CarStatsViewerScreen.kt b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/CarStatsViewerScreen.kt index d4271a0b..25d51bff 100644 --- a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/CarStatsViewerScreen.kt +++ b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/CarStatsViewerScreen.kt @@ -45,17 +45,18 @@ class CarStatsViewerScreen( private val CID_TRIP_DATA = "cid_trip_data" private val CID_MENU = "cid_menu" - private val CID_CANVAS = "cid_nav_test" + private val CID_CANVAS = "cid_canvas" private val CID_STATUS = "cid_status" internal var dataUpdate = false internal var apiState: Map = mapOf() - internal var session : DrivingSession? = null internal val carDataSurfaceCallback = CarDataSurfaceCallback(carContext) internal val appPreferences = CarStatsViewer.appPreferences + internal var drivingSession: DrivingSession? = null + internal val colorDisconnected = CarColor.createCustom(carContext.getColor(R.color.inactive_text_color), carContext.getColor(R.color.disabled_tint)) internal val colorConnected = CarColor.createCustom(carContext.getColor(R.color.connected_blue), carContext.getColor(R.color.connected_blue)) internal val colorLimited = CarColor.createCustom(carContext.getColor(R.color.limited_yellow), carContext.getColor(R.color.limited_yellow)) @@ -63,7 +64,14 @@ class CarStatsViewerScreen( internal var resetFlag = false - internal var selectedTabContentID = CID_TRIP_DATA + internal var selectedTabContentID = CID_CANVAS + set(value) { + if (field != value) { + carDataSurfaceCallback.invalidatePlot() + } + field = value + + } private val settingsActivityIntent = Intent(carContext, SettingsActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK @@ -88,6 +96,10 @@ class CarStatsViewerScreen( lifecycleScope.launch { CarStatsViewer.dataProcessor.selectedSessionDataFlow.throttle(1000).collect { carDataSurfaceCallback.updateSession() + drivingSession = it + if (selectedTabContentID != CID_CANVAS) { + invalidate() + } } } setupListeners() @@ -116,12 +128,12 @@ class CarStatsViewerScreen( override fun onPause(owner: LifecycleOwner) { super.onPause(owner) - carDataSurfaceCallback.pause() + // carDataSurfaceCallback.pause() } override fun onResume(owner: LifecycleOwner) { super.onResume(owner) - carDataSurfaceCallback.resume() + // carDataSurfaceCallback.resume() } override fun onGetTemplate(): Template { @@ -129,7 +141,6 @@ class CarStatsViewerScreen( if (dataUpdate) { InAppLogger.i("[AAOS] Data Update") this.apiState = CarStatsViewer.watchdog.getCurrentWatchdogState().apiState - session = CarStatsViewer.dataProcessor.selectedSessionData dataUpdate = false } @@ -148,10 +159,17 @@ class CarStatsViewerScreen( invalidate() } }).apply { + val tripType = when (appPreferences.mainViewTrip + 1) { + 1 -> R.string.CurrentTripData + 2 -> R.string.SinceChargeData + 3 -> R.string.AutoTripData + 4 -> R.string.CurrentMonthData + else -> R.string.car_app_unknown + } setHeaderAction(Action.APP_ICON) - addTab(createTab(R.string.car_app_trip_data, CID_TRIP_DATA, R.drawable.ic_car_app_list)) + addTab(createTab(R.string.car_app_dashboard, CID_CANVAS, R.drawable.ic_car_app_dashboard)) + addTab(createTab(tripType, CID_TRIP_DATA, R.drawable.ic_car_app_list)) addTab(createTab(R.string.car_app_status, CID_STATUS, R.drawable.ic_car_app_status)) - addTab(createTab(R.string.car_app_canvas, CID_CANVAS, R.drawable.ic_car_app_canvas)) addTab(createTab(R.string.car_app_menu, CID_MENU, R.drawable.ic_car_app_menu)) setTabContents(TabContents.Builder( // TripDataList() @@ -159,7 +177,7 @@ class CarStatsViewerScreen( when (selectedTabContentID) { CID_TRIP_DATA -> { carDataSurfaceCallback.pause() - TripDataList() + TripDataList(drivingSession) } CID_STATUS -> { carDataSurfaceCallback.pause() diff --git a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/NavigationTestTab.kt b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/NavigationTestTab.kt index 17955bc3..5e4a3bc5 100644 --- a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/NavigationTestTab.kt +++ b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/NavigationTestTab.kt @@ -36,7 +36,8 @@ internal fun CarStatsViewerScreen.NavigationTest() = NavigationTemplate.Builder( val currentDistance = appPreferences.mainPrimaryDimensionRestriction appPreferences.mainPrimaryDimensionRestriction = if (currentDistance >= 2) 0 else currentDistance + 1 invalidate() - carDataSurfaceCallback.renderFrame() + carDataSurfaceCallback.invalidatePlot() + // carDataSurfaceCallback.renderFrame() } }.build()) }.build() diff --git a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/TripDataTab.kt b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/TripDataTab.kt index 958fd166..768ae00e 100644 --- a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/TripDataTab.kt +++ b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/TripDataTab.kt @@ -17,6 +17,7 @@ import androidx.car.app.model.SectionedItemList import androidx.core.graphics.drawable.IconCompat import com.ixam97.carStatsViewer.CarStatsViewer import com.ixam97.carStatsViewer.R +import com.ixam97.carStatsViewer.database.tripData.DrivingSession import com.ixam97.carStatsViewer.database.tripData.TripType import com.ixam97.carStatsViewer.utils.InAppLogger import com.ixam97.carStatsViewer.utils.StringFormatters @@ -26,134 +27,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch @OptIn(ExperimentalCarApi::class) -internal fun CarStatsViewerScreen.TripDataPane() = PaneTemplate.Builder(Pane.Builder().apply { - session?.let { - val tripType = when (it.session_type) { - 1 -> carContext.getString(R.string.CurrentTripData) - 2 -> carContext.getString(R.string.SinceChargeData) - 3 -> carContext.getString(R.string.AutoTripData) - 4 -> carContext.getString(R.string.CurrentMonthData) - else -> "unknown" - } - val tripTypeIcon = when (it.session_type) { - 1 -> CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_hand)).build() - 2 -> CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_charger)).build() - 3 -> CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_day)).build() - 4 -> CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_month)).build() - else -> CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_help)).build() - } - addRow(createDataRow( - StringFormatters.getTraveledDistanceString(it.driven_distance.toFloat()), - carContext.getString(R.string.summary_traveled_distance), - R.drawable.ic_distance_large - )) - addRow(createDataRow( - StringFormatters.getEnergyString(it.used_energy.toFloat()), - carContext.getString(R.string.summary_used_energy), - R.drawable.ic_energy_large - )) - addRow(createDataRow( - StringFormatters.getAvgConsumptionString(it.used_energy.toFloat(), it.driven_distance.toFloat()), - carContext.getString(R.string.summary_average_consumption), - R.drawable.ic_diagram - )) - addRow(createDataRow( - StringFormatters.getAvgSpeedString(it.driven_distance.toFloat(), it.drive_time), - carContext.getString(R.string.summary_speed), - R.drawable.ic_speed_large - )) - try { - addRow( - createDataRow( - StringFormatters.getElapsedTimeString(it.drive_time, true), - carContext.getString(R.string.summary_travel_time), - R.drawable.ic_time_large - ) - ) - } catch (e: Exception) { - InAppLogger.e("Row failed: ${e.stackTraceToString()}") - } - - var statusString = "" - var index = 0 - var apiIconColor = colorDisconnected - - apiState.forEach { apiName, status -> - statusString += "$apiName: $status " - if (index == CarStatsViewer.appPreferences.mainViewConnectionApi) { - apiIconColor = when (status) { - 0 -> colorDisconnected - 1 -> colorConnected - 2 -> colorLimited - else -> colorError - } - } - - index++ - } - - addRow(createDataRow( - statusString, - "Api Status", - R.drawable.ic_connected, - apiIconColor - )) - - if (it.session_type == 1) { - addAction(Action.Builder().apply { - setIcon(CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_reset)).build()) - setTitle(carContext.getString(R.string.dialog_reset_confirm)) - setOnClickListener { - if (resetFlag) { - CoroutineScope(Dispatchers.IO).launch { - CarStatsViewer.dataProcessor.resetTrip( - TripType.MANUAL, - CarStatsViewer.dataProcessor.realTimeData.drivingState - ) - } - resetFlag = false - CarToast.makeText(carContext, carContext.getString(R.string.car_app_toast_reset_confirmation), CarToast.LENGTH_LONG).show() - } else { - CoroutineScope(Dispatchers.IO).launch { - delay(4_000) - resetFlag = false - invalidate() - } - resetFlag = true - CarToast.makeText(carContext, carContext.getString(R.string.car_app_toast_reset_hint), CarToast.LENGTH_LONG).show() - } - invalidate() - } - if (resetFlag) { - setFlags(Action.FLAG_PRIMARY) - } - }.build()) - } - - addAction(Action.Builder().apply { - setIcon(tripTypeIcon) - setTitle(tripType) - setOnClickListener { - val newTripType = if (it.session_type >= 4 ) { - 1 - } else { - it.session_type + 1 - } - CarStatsViewer.dataProcessor.changeSelectedTrip(newTripType) - CarStatsViewer.appPreferences.mainViewTrip = newTripType - 1 - // session = null - // invalidate() - } - // setFlags(Action.FLAG_PRIMARY) - }.build()) - - } ?: setLoading(true) -}.build()).apply { - setTitle("Dummy") -}.build() - -@OptIn(ExperimentalCarApi::class) -internal fun CarStatsViewerScreen.TripDataList() = ListTemplate.Builder().apply { +internal fun CarStatsViewerScreen.TripDataList(session: DrivingSession?) = ListTemplate.Builder().apply { if (session == null) { setLoading(true) @@ -165,7 +39,7 @@ internal fun CarStatsViewerScreen.TripDataList() = ListTemplate.Builder().apply 4 -> carContext.getString(R.string.CurrentMonthData) else -> "unknown" } - addSectionedList(SectionedItemList.create(ItemList.Builder().apply { + setSingleList(ItemList.Builder().apply { session?.let { addItem(createDataRow( @@ -219,7 +93,7 @@ internal fun CarStatsViewerScreen.TripDataList() = ListTemplate.Builder().apply apiIconColor )) } - }.build(), tripType)) + }.build()) addAction(Action.Builder().apply { val backgroundColor = carContext.getColor(R.color.default_button_color) @@ -253,7 +127,8 @@ internal fun CarStatsViewerScreen.TripDataList() = ListTemplate.Builder().apply }.build() -private fun CarStatsViewerScreen.createDataRow(value: String, name: String, iconResId: Int, iconColor: CarColor = CarColor.DEFAULT) = Row.Builder().apply { +@OptIn(ExperimentalCarApi::class) private +fun CarStatsViewerScreen.createDataRow(value: String, name: String, iconResId: Int, iconColor: CarColor = CarColor.DEFAULT) = Row.Builder().apply { setTitle(value) setImage(CarIcon.Builder(IconCompat.createWithResource(carContext, iconResId)).setTint(iconColor).build()) addText(name) diff --git a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/CarDataSurfaceCallback.kt b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/CarDataSurfaceCallback.kt index 9dd06e20..f398fbd5 100644 --- a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/CarDataSurfaceCallback.kt +++ b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/CarDataSurfaceCallback.kt @@ -29,21 +29,21 @@ class CarDataSurfaceCallback(val carContext: CarContext): SurfaceCallback { override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) { - Log.d(TAG, "Surface available") + InAppLogger.d("[$TAG] Surface available") super.onSurfaceAvailable(surfaceContainer) surface = surfaceContainer.surface renderFrame() } override fun onVisibleAreaChanged(visibleArea: Rect) { - Log.i(TAG, "Visible area changed " + surface + ". stableArea: " + InAppLogger.i("[$TAG] Visible area changed " + surface + ". stableArea: " + stableArea + " visibleArea:" + visibleArea) this.visibleArea = visibleArea renderFrame() } override fun onStableAreaChanged(stableArea: Rect) { - Log.i(TAG, "Stable area changed " + surface + ". stableArea: " + InAppLogger.i("[$TAG] Stable area changed " + surface + ". stableArea: " + stableArea + " visibleArea:" + visibleArea) super.onStableAreaChanged(stableArea) this.stableArea = stableArea @@ -51,22 +51,36 @@ class CarDataSurfaceCallback(val carContext: CarContext): SurfaceCallback { } fun pause() { + renderFrame(clearFrame = true) rendererEnabled = false } fun resume() { rendererEnabled = true + invalidatePlot() } - fun renderFrame() { + fun renderFrame(clearFrame: Boolean = false) { if (!rendererEnabled) return - Log.d(TAG, "Rendering Frame") + InAppLogger.d("[$TAG] Rendering Frame") defaultRenderer.setData(CarStatsViewer.dataProcessor.realTimeData) surface?.apply { if(!isValid) return - val canvas: Canvas = lockCanvas(null) + val canvas: Canvas = try { + lockCanvas(null) + } catch (e: Exception) { + InAppLogger.w("[$TAG] Failed to get canvas:\n${e.printStackTrace()}") + return + } + + if (clearFrame) { + canvas.drawColor(Color.BLACK) + unlockCanvasAndPost(canvas) + return + } + canvas.drawColor(carContext.getColor(R.color.slideup_activity_background)) // canvas.drawColor(if (carContext.isDarkMode) Color.BLACK else Color.WHITE) @@ -83,4 +97,9 @@ class CarDataSurfaceCallback(val carContext: CarContext): SurfaceCallback { defaultRenderer.updateSession() renderFrame() } + + fun invalidatePlot() { + defaultRenderer.refreshConsumptionPlot() + renderFrame() + } } \ No newline at end of file diff --git a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/DefaultRenderer.kt b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/DefaultRenderer.kt index 028b79ee..0964fbb3 100644 --- a/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/DefaultRenderer.kt +++ b/automotive/src/main/java/com/ixam97/carStatsViewer/carApp/renderer/DefaultRenderer.kt @@ -94,8 +94,8 @@ class DefaultRenderer(val carContext: CarContext): Renderer { } consGage.apply { - valueTextSize = 115f - descriptionTextSize = 31f + valueTextSize = 110f + descriptionTextSize = 28f } diagram.apply { @@ -160,7 +160,7 @@ class DefaultRenderer(val carContext: CarContext): Renderer { mRightInsetPaint ) } else { - Log.d(TAG, "Visible area not available.") + InAppLogger.d("[$TAG] Visible area not available.") } if (stableArea != null) { // Draw a cross-hairs at the stable center. @@ -189,7 +189,7 @@ class DefaultRenderer(val carContext: CarContext): Renderer { mCenterPaint ) } else { - Log.d(TAG, "Stable area not available.") + InAppLogger.d("[$TAG] Stable area not available.") } } @@ -207,17 +207,17 @@ class DefaultRenderer(val carContext: CarContext): Renderer { val isLandscape = (canvas.width > canvas.height) - Log.d(TAG, "Rendering Frame") + InAppLogger.d("[$TAG] Rendering Frame") val density = CarStatsViewer.appContext.resources.displayMetrics.density - Log.d(TAG, "Density: $density") + InAppLogger.d("[$TAG] Density: $density") canvas.scale(density, density) - var correctedArea = if (stableArea != null) { + var correctedArea = if (visibleArea != null) { Rect( - (stableArea.left / density).toInt(), - (stableArea.top / density).toInt() + if (!isLandscape) topCorrection else 0, - (stableArea.right / density).toInt(), - (stableArea.bottom / density).toInt()) + (visibleArea.left / density).toInt(), + (visibleArea.top / density).toInt() + if (!isLandscape) topCorrection else 0, + (visibleArea.right / density).toInt(), + (visibleArea.bottom / density).toInt()) } else null if (visibleArea != null) { @@ -289,12 +289,25 @@ class DefaultRenderer(val carContext: CarContext): Renderer { var consRect = consGage.drawGage(canvas, xOffset = if (horizontalOverflowFlag || isLandscape) 0 else 320, yOffset = if (horizontalOverflowFlag || isLandscape) powerRect.bottom + 15 else 0) // drawBoundingBox(canvas, consRect, consRect) - if (consRect.right > (correctedArea?.right?:0) && !horizontalOverflowFlag) { - Log.w(TAG, "Overflow detected. Setting flag for rearrangement") - horizontalOverflowFlag = true - canvas.drawColor(carContext.getColor(R.color.slideup_activity_background)) - powerRect = powerGage.drawGage(canvas) - consRect = consGage.drawGage(canvas, xOffset = if (horizontalOverflowFlag || isLandscape) 0 else 320, yOffset = if (horizontalOverflowFlag || isLandscape) powerRect.bottom + 15 else 0) + // drawBoundingBox(canvas, correctedArea, correctedArea) + + when (CarStatsViewer.dataProcessor.staticVehicleData.modelName) { + "Polestar 2" -> horizontalOverflowFlag = false + "EX40", "EC40" -> horizontalOverflowFlag = true + else -> { + if ((consRect.right) > (correctedArea?.right?:0) && !horizontalOverflowFlag) { + InAppLogger.w("[$TAG] Overflow detected: ${consRect.right}, ${(correctedArea?.right?:0)}. Setting flag for rearrangement") + horizontalOverflowFlag = true + canvas.drawColor(carContext.getColor(R.color.slideup_activity_background)) + powerRect = powerGage.drawGage(canvas) + consRect = consGage.drawGage(canvas, xOffset = if (horizontalOverflowFlag || isLandscape) 0 else 320, yOffset = if (horizontalOverflowFlag || isLandscape) powerRect.bottom + 15 else 0) + } else if ((consRect.right + powerRect.right) < (correctedArea?.right?:0) && horizontalOverflowFlag) { + horizontalOverflowFlag = false + canvas.drawColor(carContext.getColor(R.color.slideup_activity_background)) + powerRect = powerGage.drawGage(canvas) + consRect = consGage.drawGage(canvas, xOffset = if (horizontalOverflowFlag || isLandscape) 0 else 320, yOffset = if (horizontalOverflowFlag || isLandscape) powerRect.bottom + 15 else 0) + } + } } val plotRect = Rect( @@ -306,7 +319,7 @@ class DefaultRenderer(val carContext: CarContext): Renderer { correctedArea = moveDrawingArea(canvas, drawBox = false, stableArea = plotRect) - Log.d(TAG, "Canvas dimensions: ${canvas.width} x ${canvas.height}") + InAppLogger.d("[$TAG] Canvas dimensions: ${canvas.width} x ${canvas.height}") val diagramBounds: Rect? = if (correctedArea != null) { Rect(correctedArea.left,correctedArea.top, correctedArea.right, correctedArea.bottom - 15) } else null @@ -329,7 +342,7 @@ class DefaultRenderer(val carContext: CarContext): Renderer { canvas.translate(mDx.toFloat(), mDy.toFloat()) Rect(0, 0, stableArea.right - mDx, stableArea.bottom - mDy) } else { - Log.w(TAG, "Invalid drawing area movement!") + InAppLogger.w("[$TAG] Invalid drawing area movement!") null } @@ -383,7 +396,7 @@ class DefaultRenderer(val carContext: CarContext): Renderer { } } - private fun refreshConsumptionPlot(drivingPoints: List = emptyList()) { + fun refreshConsumptionPlot(drivingPoints: List = emptyList()) { val appPreferences = CarStatsViewer.appPreferences InAppLogger.d("Refreshing entire consumption plot.") var localDrivingPoints = drivingPoints diff --git a/automotive/src/main/res/drawable/ic_car_app_dashboard.xml b/automotive/src/main/res/drawable/ic_car_app_dashboard.xml new file mode 100644 index 00000000..c474227b --- /dev/null +++ b/automotive/src/main/res/drawable/ic_car_app_dashboard.xml @@ -0,0 +1,9 @@ + + + diff --git a/automotive/src/main/res/drawable/ic_car_app_settings.xml b/automotive/src/main/res/drawable/ic_car_app_settings.xml new file mode 100644 index 00000000..8d05030e --- /dev/null +++ b/automotive/src/main/res/drawable/ic_car_app_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/automotive/src/main/res/values/strings.xml b/automotive/src/main/res/values/strings.xml index 26039531..af119e8b 100644 --- a/automotive/src/main/res/values/strings.xml +++ b/automotive/src/main/res/values/strings.xml @@ -360,4 +360,5 @@ Manual Trip has been reset. Press again to reset manual Trip. Canvas Test + Unknown