Skip to content

Commit

Permalink
Add support for partial table updates
Browse files Browse the repository at this point in the history
  • Loading branch information
hufman committed Jan 13, 2024
1 parent 7afbbf8 commit 6a38086
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 21 deletions.
63 changes: 59 additions & 4 deletions android/src/main/kotlin/io/bimmergestalt/headunit/Pigeon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,43 @@ data class RHMIAppInfo (
}
}

/** Generated class from Pigeon that represents data sent in messages. */
data class RHMITableUpdate (
val totalRows: Long,
val totalColumns: Long,
val startRow: Long,
val startColumn: Long,
val numRows: Long,
val numColumns: Long,
val data: List<List<Any?>?>

) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): RHMITableUpdate {
val totalRows = list[0].let { if (it is Int) it.toLong() else it as Long }
val totalColumns = list[1].let { if (it is Int) it.toLong() else it as Long }
val startRow = list[2].let { if (it is Int) it.toLong() else it as Long }
val startColumn = list[3].let { if (it is Int) it.toLong() else it as Long }
val numRows = list[4].let { if (it is Int) it.toLong() else it as Long }
val numColumns = list[5].let { if (it is Int) it.toLong() else it as Long }
val data = list[6] as List<List<Any?>?>
return RHMITableUpdate(totalRows, totalColumns, startRow, startColumn, numRows, numColumns, data)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
totalRows,
totalColumns,
startRow,
startColumn,
numRows,
numColumns,
data,
)
}
}

/** Generated class from Pigeon that represents data sent in messages. */
data class RHMIImageId (
val id: Long
Expand Down Expand Up @@ -157,6 +194,11 @@ private object ServerApiCodec : StandardMessageCodec() {
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RHMITableUpdate.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RHMITextId.fromList(it)
}
Expand All @@ -178,10 +220,14 @@ private object ServerApiCodec : StandardMessageCodec() {
stream.write(130)
writeValue(stream, value.toList())
}
is RHMITextId -> {
is RHMITableUpdate -> {
stream.write(131)
writeValue(stream, value.toList())
}
is RHMITextId -> {
stream.write(132)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
Expand Down Expand Up @@ -322,6 +368,11 @@ private object HeadunitApiCodec : StandardMessageCodec() {
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RHMITableUpdate.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RHMITextId.fromList(it)
}
Expand All @@ -343,10 +394,14 @@ private object HeadunitApiCodec : StandardMessageCodec() {
stream.write(130)
writeValue(stream, value.toList())
}
is RHMITextId -> {
is RHMITableUpdate -> {
stream.write(131)
writeValue(stream, value.toList())
}
is RHMITextId -> {
stream.write(132)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
Expand Down Expand Up @@ -403,9 +458,9 @@ class HeadunitApi(private val binaryMessenger: BinaryMessenger) {
callback()
}
}
fun _dummy(aArg: RHMITextId, bArg: RHMIImageId, callback: () -> Unit) {
fun _dummy(aArg: RHMITextId, bArg: RHMIImageId, cArg: RHMITableUpdate, callback: () -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.headunit.HeadunitApi._dummy", codec)
channel.send(listOf(aArg, bArg)) {
channel.send(listOf(aArg, bArg, cArg)) {
callback()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import de.bmw.idrive.BMWRemotingClient
import io.bimmergestalt.headunit.AMAppInfo
import io.bimmergestalt.headunit.HeadunitCallbacks
import io.flutter.Log
import java.util.concurrent.ConcurrentHashMap

class AMManager(val callbacks: HeadunitCallbacks) {
private val TAG = "AMManager"
private val knownApps = HashMap<String, AMAppInfo>()
private val eventHandlers = HashMap<Int, BMWRemotingClient>()
private val knownApps = ConcurrentHashMap<String, AMAppInfo>()
private val eventHandlers = ConcurrentHashMap<Int, BMWRemotingClient>()

fun registerApp(handle: Int, appId: String, name: String, icon: ByteArray, category: String) {
val existing = knownApps[appId]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import io.bimmergestalt.headunit.HeadunitCallbacks
import io.bimmergestalt.headunit.RHMIAppInfo
import io.bimmergestalt.headunit.RHMIImageId
import io.bimmergestalt.headunit.RHMITextId
import io.bimmergestalt.headunit.RHMITableUpdate
import io.flutter.Log
import java.util.concurrent.ConcurrentHashMap

class RHMIManager(val callbacks: HeadunitCallbacks) {
private val TAG = "RHMIManager"
private val knownApps = HashMap<String, RHMIAppInfo>()
private val actionHandlers = HashMap<Int, BMWRemotingClient>()
private val eventHandlers = HashMap<Int, BMWRemotingClient>()
private val actionCallbacks = HashMap<Int, HashMap<Int, (Result<Boolean>) -> Unit>>()
private val knownApps = ConcurrentHashMap<String, RHMIAppInfo>()
private val actionHandlers = ConcurrentHashMap<Int, BMWRemotingClient>()
private val eventHandlers = ConcurrentHashMap<Int, BMWRemotingClient>()
private val actionCallbacks = ConcurrentHashMap<Int, ConcurrentHashMap<Int, (Result<Boolean>) -> Unit>>()

fun registerApp(handle: Int, appId: String, resources: Map<RHMIResourceType, ByteArray>) {
val existing = knownApps[appId]
Expand Down Expand Up @@ -67,11 +69,16 @@ class RHMIManager(val callbacks: HeadunitCallbacks) {
return when (value) {
is RHMIDataTable -> {
// TODO handle partial table updates :fear:
value.data.map { row ->
val data = value.data.map { row ->
row.map { cell ->
simplifyData(cell)
}
}
RHMITableUpdate(totalRows = value.totalRows.toLong(), totalColumns = value.totalColumns.toLong(),
startRow = value.fromRow.toLong(), startColumn = value.fromColumn.toLong(),
numRows = value.numRows.toLong(), numColumns = value.numColumns.toLong(),
data = data
)
}
is RHMIResourceData -> value.data // assume the destination model is RA
is RHMIResourceIdentifier -> if (value.type == RHMIResourceType.IMAGEID) {
Expand Down Expand Up @@ -112,7 +119,7 @@ class RHMIManager(val callbacks: HeadunitCallbacks) {
actionHandlers[existing.handle.toInt()]?.rhmi_onActionEvent(existing.handle.toInt(), "", actionId, args)

if (!actionCallbacks.containsKey(existing.handle.toInt())) {
actionCallbacks[existing.handle.toInt()] = HashMap()
actionCallbacks[existing.handle.toInt()] = ConcurrentHashMap()
}
actionCallbacks[existing.handle.toInt()]?.put(actionId, callback)
}
Expand Down
36 changes: 35 additions & 1 deletion app/lib/rhmi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:default_map/default_map.dart';
import 'package:flutter/material.dart';
import 'package:headunit/pigeon.dart';
import 'package:xml/xml.dart';

class RHMIApp {
Expand Down Expand Up @@ -144,7 +145,39 @@ class RHMIAppDescription {
log("Unknown model $model from ${models.keys}");
}
log("Setting data $model to $value");
models[model]?.value = value;
if (value is RHMITableUpdate) {
// check existing model value and clear it if the table shape is different
var destTable = models[model]?.value;
if (destTable is List<List>) {
if (destTable.isEmpty || destTable.length != value.totalRows || destTable[0].length != value.totalColumns) {
destTable = null;
}
} else {
destTable = null;
}

// create a table
destTable ??= List.generate(value.totalRows, (index) => List<Object?>.filled(value.totalColumns, null, growable: true));

// fill the table
if (destTable is List<List<Object?>>) {
for (final (rowIndex, row) in value.data.indexed) {
if (row is List) {
for (final (colIndex, col) in row.indexed) {
if (value.startColumn + colIndex >= destTable[value.startRow + rowIndex].length) {
destTable[value.startRow + rowIndex].add(col);
} else {
destTable[value.startRow + rowIndex][value.startColumn + colIndex] = col;
}
}
}
}
}
models[model]?.value = null;
models[model]?.value = destTable;
} else {
models[model]?.value = value;
}
}
}

Expand Down Expand Up @@ -283,6 +316,7 @@ class RHMIProperty extends ValueNotifier<Object?> {
static const enabled = 1;
static const selectable = 2;
static const visible = 3;
static const valid = 4;
static const list_columnwidth = 6;
static const width = 9;
static const height = 10;
Expand Down
18 changes: 15 additions & 3 deletions app/lib/rhmi_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -549,15 +549,18 @@ class RHMIListWidget extends RHMIComponentWidget {
final RHMICallbacks callbacks;
final String modelName;

Widget renderCell(Object value, {required bool darkMode}) {
Widget renderCell(Object? value, {required bool darkMode}) {
if (value is RHMITextId) {
return RHMITextIdWidget(app: app, textId: value.id);
} else if (value is RHMIImageId) {
return RHMIImageIdWidget(app: app, imageId: value.id, width: 48, height: 48);
} else if (value is Uint8List) {
return TransparentIcon(iconData: value, darkMode: darkMode, width: 96, height: 96);
} else {
return TransparentIcon(
iconData: value, darkMode: darkMode, width: 96, height: 96);
} else if (value != null) {
return Text(value.toString());
} else {
return const Text("");
}
}

Expand All @@ -574,6 +577,15 @@ class RHMIListWidget extends RHMIComponentWidget {
final darkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
final value = model.value;
if (value is List) {
if (component.properties[RHMIProperty.valid].value == false) {
for (final (_, row) in value.indexed) {
if (row is List && row[0] == null) {
log("Component ${component.id} requesting data for ${value.length} rows");
callbacks.client.rhmiEvent(app.appId, component.id, 2, {5: 0, 6: value.length}); // load partial data
break;
}
}
}
final columnSizes = component.properties[RHMIProperty.list_columnwidth].value.toString()
.split(",").map((e) => int.tryParse(e)?.let((self) => FixedColumnWidth(self.toDouble())) ?? const FlexColumnWidth())
.toList().asMap();
Expand Down
72 changes: 68 additions & 4 deletions lib/pigeon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,57 @@ class RHMIAppInfo {
}
}

class RHMITableUpdate {
RHMITableUpdate({
required this.totalRows,
required this.totalColumns,
required this.startRow,
required this.startColumn,
required this.numRows,
required this.numColumns,
required this.data,
});

int totalRows;

int totalColumns;

int startRow;

int startColumn;

int numRows;

int numColumns;

List<List?> data;

Object encode() {
return <Object?>[
totalRows,
totalColumns,
startRow,
startColumn,
numRows,
numColumns,
data,
];
}

static RHMITableUpdate decode(Object result) {
result as List<Object?>;
return RHMITableUpdate(
totalRows: result[0]! as int,
totalColumns: result[1]! as int,
startRow: result[2]! as int,
startColumn: result[3]! as int,
numRows: result[4]! as int,
numColumns: result[5]! as int,
data: (result[6] as List<Object?>?)!.cast<List?>(),
);
}
}

class RHMIImageId {
RHMIImageId({
required this.id,
Expand Down Expand Up @@ -135,9 +186,12 @@ class _ServerApiCodec extends StandardMessageCodec {
} else if (value is RHMIImageId) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else if (value is RHMITextId) {
} else if (value is RHMITableUpdate) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
} else if (value is RHMITextId) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
Expand All @@ -153,6 +207,8 @@ class _ServerApiCodec extends StandardMessageCodec {
case 130:
return RHMIImageId.decode(readValue(buffer)!);
case 131:
return RHMITableUpdate.decode(readValue(buffer)!);
case 132:
return RHMITextId.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
Expand Down Expand Up @@ -304,9 +360,12 @@ class _HeadunitApiCodec extends StandardMessageCodec {
} else if (value is RHMIImageId) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else if (value is RHMITextId) {
} else if (value is RHMITableUpdate) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
} else if (value is RHMITextId) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
Expand All @@ -322,6 +381,8 @@ class _HeadunitApiCodec extends StandardMessageCodec {
case 130:
return RHMIImageId.decode(readValue(buffer)!);
case 131:
return RHMITableUpdate.decode(readValue(buffer)!);
case 132:
return RHMITextId.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
Expand All @@ -346,7 +407,7 @@ abstract class HeadunitApi {

void rhmiTriggerEvent(String appId, int eventId, Map<int?, Object?> args);

void _dummy(RHMITextId a, RHMIImageId b);
void _dummy(RHMITextId a, RHMIImageId b, RHMITableUpdate c);

static void setup(HeadunitApi? api, {BinaryMessenger? binaryMessenger}) {
{
Expand Down Expand Up @@ -516,7 +577,10 @@ abstract class HeadunitApi {
final RHMIImageId? arg_b = (args[1] as RHMIImageId?);
assert(arg_b != null,
'Argument for dev.flutter.pigeon.headunit.HeadunitApi._dummy was null, expected non-null RHMIImageId.');
api._dummy(arg_a!, arg_b!);
final RHMITableUpdate? arg_c = (args[2] as RHMITableUpdate?);
assert(arg_c != null,
'Argument for dev.flutter.pigeon.headunit.HeadunitApi._dummy was null, expected non-null RHMITableUpdate.');
api._dummy(arg_a!, arg_b!, arg_c!);
return;
});
}
Expand Down
Loading

0 comments on commit 6a38086

Please sign in to comment.