autoconnect on fly mode (does not work yet)
This commit is contained in:
@@ -84,6 +84,13 @@ class AppSettingsTable extends Table {
|
||||
TextColumn get connectionWifiSsidPrefix =>
|
||||
text().withDefault(const Constant(kDefaultWifiSsidPrefix))();
|
||||
|
||||
/// Exakte SSID der zuletzt erfolgreich verbundenen mLRS-Bruecke (Doku
|
||||
/// Abschnitt 8: "verbundenes WLAN merken") - null, solange noch nie
|
||||
/// erfolgreich verbunden wurde. Wird fuer kuenftige Verbindungsversuche
|
||||
/// als exakter WifiNetworkSpecifier statt Praefix verwendet, damit Android
|
||||
/// ohne erneuten Systemdialog automatisch verbinden kann.
|
||||
TextColumn get connectionWifiLastSsid => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
@@ -95,7 +102,7 @@ class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase.forTesting(super.executor);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 5;
|
||||
int get schemaVersion => 6;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
@@ -133,6 +140,12 @@ class AppDatabase extends _$AppDatabase {
|
||||
appSettingsTable.connectionWifiSsidPrefix,
|
||||
);
|
||||
}
|
||||
if (from < 6) {
|
||||
await m.addColumn(
|
||||
appSettingsTable,
|
||||
appSettingsTable.connectionWifiLastSsid,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1394,6 +1394,17 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant(kDefaultWifiSsidPrefix),
|
||||
);
|
||||
static const VerificationMeta _connectionWifiLastSsidMeta =
|
||||
const VerificationMeta('connectionWifiLastSsid');
|
||||
@override
|
||||
late final GeneratedColumn<String> connectionWifiLastSsid =
|
||||
GeneratedColumn<String>(
|
||||
'connection_wifi_last_ssid',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
@@ -1405,6 +1416,7 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
connectionWifiHost,
|
||||
connectionWifiPort,
|
||||
connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@@ -1493,6 +1505,15 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('connection_wifi_last_ssid')) {
|
||||
context.handle(
|
||||
_connectionWifiLastSsidMeta,
|
||||
connectionWifiLastSsid.isAcceptableOrUnknown(
|
||||
data['connection_wifi_last_ssid']!,
|
||||
_connectionWifiLastSsidMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1538,6 +1559,10 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}connection_wifi_ssid_prefix'],
|
||||
)!,
|
||||
connectionWifiLastSsid: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}connection_wifi_last_ssid'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1578,6 +1603,13 @@ class AppSettingsTableData extends DataClass
|
||||
/// `WifiNetworkSpecifier` (Doku Kommunikationsschicht v2 Abschnitt 4/8),
|
||||
/// Standard "mLRS-", ueberschreibbar.
|
||||
final String connectionWifiSsidPrefix;
|
||||
|
||||
/// Exakte SSID der zuletzt erfolgreich verbundenen mLRS-Bruecke (Doku
|
||||
/// Abschnitt 8: "verbundenes WLAN merken") - null, solange noch nie
|
||||
/// erfolgreich verbunden wurde. Wird fuer kuenftige Verbindungsversuche
|
||||
/// als exakter WifiNetworkSpecifier statt Praefix verwendet, damit Android
|
||||
/// ohne erneuten Systemdialog automatisch verbinden kann.
|
||||
final String? connectionWifiLastSsid;
|
||||
const AppSettingsTableData({
|
||||
required this.id,
|
||||
this.activeDroneProfileId,
|
||||
@@ -1588,6 +1620,7 @@ class AppSettingsTableData extends DataClass
|
||||
this.connectionWifiHost,
|
||||
required this.connectionWifiPort,
|
||||
required this.connectionWifiSsidPrefix,
|
||||
this.connectionWifiLastSsid,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
@@ -1613,6 +1646,11 @@ class AppSettingsTableData extends DataClass
|
||||
map['connection_wifi_ssid_prefix'] = Variable<String>(
|
||||
connectionWifiSsidPrefix,
|
||||
);
|
||||
if (!nullToAbsent || connectionWifiLastSsid != null) {
|
||||
map['connection_wifi_last_ssid'] = Variable<String>(
|
||||
connectionWifiLastSsid,
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -1635,6 +1673,9 @@ class AppSettingsTableData extends DataClass
|
||||
: Value(connectionWifiHost),
|
||||
connectionWifiPort: Value(connectionWifiPort),
|
||||
connectionWifiSsidPrefix: Value(connectionWifiSsidPrefix),
|
||||
connectionWifiLastSsid: connectionWifiLastSsid == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(connectionWifiLastSsid),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1663,6 +1704,9 @@ class AppSettingsTableData extends DataClass
|
||||
connectionWifiSsidPrefix: serializer.fromJson<String>(
|
||||
json['connectionWifiSsidPrefix'],
|
||||
),
|
||||
connectionWifiLastSsid: serializer.fromJson<String?>(
|
||||
json['connectionWifiLastSsid'],
|
||||
),
|
||||
);
|
||||
}
|
||||
@override
|
||||
@@ -1682,6 +1726,9 @@ class AppSettingsTableData extends DataClass
|
||||
'connectionWifiSsidPrefix': serializer.toJson<String>(
|
||||
connectionWifiSsidPrefix,
|
||||
),
|
||||
'connectionWifiLastSsid': serializer.toJson<String?>(
|
||||
connectionWifiLastSsid,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1695,6 +1742,7 @@ class AppSettingsTableData extends DataClass
|
||||
Value<String?> connectionWifiHost = const Value.absent(),
|
||||
int? connectionWifiPort,
|
||||
String? connectionWifiSsidPrefix,
|
||||
Value<String?> connectionWifiLastSsid = const Value.absent(),
|
||||
}) => AppSettingsTableData(
|
||||
id: id ?? this.id,
|
||||
activeDroneProfileId: activeDroneProfileId.present
|
||||
@@ -1714,6 +1762,9 @@ class AppSettingsTableData extends DataClass
|
||||
connectionWifiPort: connectionWifiPort ?? this.connectionWifiPort,
|
||||
connectionWifiSsidPrefix:
|
||||
connectionWifiSsidPrefix ?? this.connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid: connectionWifiLastSsid.present
|
||||
? connectionWifiLastSsid.value
|
||||
: this.connectionWifiLastSsid,
|
||||
);
|
||||
AppSettingsTableData copyWithCompanion(AppSettingsTableCompanion data) {
|
||||
return AppSettingsTableData(
|
||||
@@ -1742,6 +1793,9 @@ class AppSettingsTableData extends DataClass
|
||||
connectionWifiSsidPrefix: data.connectionWifiSsidPrefix.present
|
||||
? data.connectionWifiSsidPrefix.value
|
||||
: this.connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid: data.connectionWifiLastSsid.present
|
||||
? data.connectionWifiLastSsid.value
|
||||
: this.connectionWifiLastSsid,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1756,7 +1810,8 @@ class AppSettingsTableData extends DataClass
|
||||
..write('connectionType: $connectionType, ')
|
||||
..write('connectionWifiHost: $connectionWifiHost, ')
|
||||
..write('connectionWifiPort: $connectionWifiPort, ')
|
||||
..write('connectionWifiSsidPrefix: $connectionWifiSsidPrefix')
|
||||
..write('connectionWifiSsidPrefix: $connectionWifiSsidPrefix, ')
|
||||
..write('connectionWifiLastSsid: $connectionWifiLastSsid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
@@ -1772,6 +1827,7 @@ class AppSettingsTableData extends DataClass
|
||||
connectionWifiHost,
|
||||
connectionWifiPort,
|
||||
connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@@ -1785,7 +1841,8 @@ class AppSettingsTableData extends DataClass
|
||||
other.connectionType == this.connectionType &&
|
||||
other.connectionWifiHost == this.connectionWifiHost &&
|
||||
other.connectionWifiPort == this.connectionWifiPort &&
|
||||
other.connectionWifiSsidPrefix == this.connectionWifiSsidPrefix);
|
||||
other.connectionWifiSsidPrefix == this.connectionWifiSsidPrefix &&
|
||||
other.connectionWifiLastSsid == this.connectionWifiLastSsid);
|
||||
}
|
||||
|
||||
class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
@@ -1798,6 +1855,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
final Value<String?> connectionWifiHost;
|
||||
final Value<int> connectionWifiPort;
|
||||
final Value<String> connectionWifiSsidPrefix;
|
||||
final Value<String?> connectionWifiLastSsid;
|
||||
const AppSettingsTableCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.activeDroneProfileId = const Value.absent(),
|
||||
@@ -1808,6 +1866,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
this.connectionWifiHost = const Value.absent(),
|
||||
this.connectionWifiPort = const Value.absent(),
|
||||
this.connectionWifiSsidPrefix = const Value.absent(),
|
||||
this.connectionWifiLastSsid = const Value.absent(),
|
||||
});
|
||||
AppSettingsTableCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
@@ -1819,6 +1878,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
this.connectionWifiHost = const Value.absent(),
|
||||
this.connectionWifiPort = const Value.absent(),
|
||||
this.connectionWifiSsidPrefix = const Value.absent(),
|
||||
this.connectionWifiLastSsid = const Value.absent(),
|
||||
});
|
||||
static Insertable<AppSettingsTableData> custom({
|
||||
Expression<int>? id,
|
||||
@@ -1830,6 +1890,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
Expression<String>? connectionWifiHost,
|
||||
Expression<int>? connectionWifiPort,
|
||||
Expression<String>? connectionWifiSsidPrefix,
|
||||
Expression<String>? connectionWifiLastSsid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
@@ -1847,6 +1908,8 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
'connection_wifi_port': connectionWifiPort,
|
||||
if (connectionWifiSsidPrefix != null)
|
||||
'connection_wifi_ssid_prefix': connectionWifiSsidPrefix,
|
||||
if (connectionWifiLastSsid != null)
|
||||
'connection_wifi_last_ssid': connectionWifiLastSsid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1860,6 +1923,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
Value<String?>? connectionWifiHost,
|
||||
Value<int>? connectionWifiPort,
|
||||
Value<String>? connectionWifiSsidPrefix,
|
||||
Value<String?>? connectionWifiLastSsid,
|
||||
}) {
|
||||
return AppSettingsTableCompanion(
|
||||
id: id ?? this.id,
|
||||
@@ -1874,6 +1938,8 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
connectionWifiPort: connectionWifiPort ?? this.connectionWifiPort,
|
||||
connectionWifiSsidPrefix:
|
||||
connectionWifiSsidPrefix ?? this.connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid:
|
||||
connectionWifiLastSsid ?? this.connectionWifiLastSsid,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1915,6 +1981,11 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
connectionWifiSsidPrefix.value,
|
||||
);
|
||||
}
|
||||
if (connectionWifiLastSsid.present) {
|
||||
map['connection_wifi_last_ssid'] = Variable<String>(
|
||||
connectionWifiLastSsid.value,
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -1929,7 +2000,8 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsTableData> {
|
||||
..write('connectionType: $connectionType, ')
|
||||
..write('connectionWifiHost: $connectionWifiHost, ')
|
||||
..write('connectionWifiPort: $connectionWifiPort, ')
|
||||
..write('connectionWifiSsidPrefix: $connectionWifiSsidPrefix')
|
||||
..write('connectionWifiSsidPrefix: $connectionWifiSsidPrefix, ')
|
||||
..write('connectionWifiLastSsid: $connectionWifiLastSsid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
@@ -2574,6 +2646,7 @@ typedef $$AppSettingsTableTableCreateCompanionBuilder =
|
||||
Value<String?> connectionWifiHost,
|
||||
Value<int> connectionWifiPort,
|
||||
Value<String> connectionWifiSsidPrefix,
|
||||
Value<String?> connectionWifiLastSsid,
|
||||
});
|
||||
typedef $$AppSettingsTableTableUpdateCompanionBuilder =
|
||||
AppSettingsTableCompanion Function({
|
||||
@@ -2586,6 +2659,7 @@ typedef $$AppSettingsTableTableUpdateCompanionBuilder =
|
||||
Value<String?> connectionWifiHost,
|
||||
Value<int> connectionWifiPort,
|
||||
Value<String> connectionWifiSsidPrefix,
|
||||
Value<String?> connectionWifiLastSsid,
|
||||
});
|
||||
|
||||
class $$AppSettingsTableTableFilterComposer
|
||||
@@ -2641,6 +2715,11 @@ class $$AppSettingsTableTableFilterComposer
|
||||
column: $table.connectionWifiSsidPrefix,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get connectionWifiLastSsid => $composableBuilder(
|
||||
column: $table.connectionWifiLastSsid,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$AppSettingsTableTableOrderingComposer
|
||||
@@ -2696,6 +2775,11 @@ class $$AppSettingsTableTableOrderingComposer
|
||||
column: $table.connectionWifiSsidPrefix,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get connectionWifiLastSsid => $composableBuilder(
|
||||
column: $table.connectionWifiLastSsid,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$AppSettingsTableTableAnnotationComposer
|
||||
@@ -2749,6 +2833,11 @@ class $$AppSettingsTableTableAnnotationComposer
|
||||
column: $table.connectionWifiSsidPrefix,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get connectionWifiLastSsid => $composableBuilder(
|
||||
column: $table.connectionWifiLastSsid,
|
||||
builder: (column) => column,
|
||||
);
|
||||
}
|
||||
|
||||
class $$AppSettingsTableTableTableManager
|
||||
@@ -2797,6 +2886,7 @@ class $$AppSettingsTableTableTableManager
|
||||
Value<String?> connectionWifiHost = const Value.absent(),
|
||||
Value<int> connectionWifiPort = const Value.absent(),
|
||||
Value<String> connectionWifiSsidPrefix = const Value.absent(),
|
||||
Value<String?> connectionWifiLastSsid = const Value.absent(),
|
||||
}) => AppSettingsTableCompanion(
|
||||
id: id,
|
||||
activeDroneProfileId: activeDroneProfileId,
|
||||
@@ -2807,6 +2897,7 @@ class $$AppSettingsTableTableTableManager
|
||||
connectionWifiHost: connectionWifiHost,
|
||||
connectionWifiPort: connectionWifiPort,
|
||||
connectionWifiSsidPrefix: connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid: connectionWifiLastSsid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
@@ -2819,6 +2910,7 @@ class $$AppSettingsTableTableTableManager
|
||||
Value<String?> connectionWifiHost = const Value.absent(),
|
||||
Value<int> connectionWifiPort = const Value.absent(),
|
||||
Value<String> connectionWifiSsidPrefix = const Value.absent(),
|
||||
Value<String?> connectionWifiLastSsid = const Value.absent(),
|
||||
}) => AppSettingsTableCompanion.insert(
|
||||
id: id,
|
||||
activeDroneProfileId: activeDroneProfileId,
|
||||
@@ -2829,6 +2921,7 @@ class $$AppSettingsTableTableTableManager
|
||||
connectionWifiHost: connectionWifiHost,
|
||||
connectionWifiPort: connectionWifiPort,
|
||||
connectionWifiSsidPrefix: connectionWifiSsidPrefix,
|
||||
connectionWifiLastSsid: connectionWifiLastSsid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||
|
||||
@@ -23,6 +23,7 @@ class AppSettingsRepository {
|
||||
String? connectionWifiHost,
|
||||
int connectionWifiPort,
|
||||
String connectionWifiSsidPrefix,
|
||||
String? connectionWifiLastSsid,
|
||||
})> load() async {
|
||||
final row = await (_db.select(_db.appSettingsTable)
|
||||
..where((s) => s.id.equals(_rowId)))
|
||||
@@ -40,6 +41,7 @@ class AppSettingsRepository {
|
||||
connectionWifiPort: row?.connectionWifiPort ?? kDefaultWifiPort,
|
||||
connectionWifiSsidPrefix:
|
||||
row?.connectionWifiSsidPrefix ?? kDefaultWifiSsidPrefix,
|
||||
connectionWifiLastSsid: row?.connectionWifiLastSsid,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,6 +121,18 @@ class AppSettingsRepository {
|
||||
));
|
||||
}
|
||||
|
||||
/// Exakte SSID der zuletzt erfolgreich verbundenen mLRS-Bruecke (Doku
|
||||
/// Abschnitt 8), oder null solange noch nie erfolgreich verbunden wurde.
|
||||
Future<String?> loadConnectionWifiLastSsid() async =>
|
||||
(await load()).connectionWifiLastSsid;
|
||||
|
||||
Future<void> setConnectionWifiLastSsid(String? ssid) async {
|
||||
await _upsert(AppSettingsTableCompanion(
|
||||
id: const Value(_rowId),
|
||||
connectionWifiLastSsid: Value(ssid),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _upsert(AppSettingsTableCompanion companion) async {
|
||||
await _db.into(_db.appSettingsTable).insertOnConflictUpdate(companion);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,19 @@ sealed class MlrsNetworkEvent {
|
||||
const MlrsNetworkEvent();
|
||||
}
|
||||
|
||||
/// Das Netz ist verfuegbar (Nutzer hat den Systemdialog bestaetigt).
|
||||
/// Das Netz ist verfuegbar (Nutzer hat den Systemdialog bestaetigt, oder
|
||||
/// Android hat eine zuvor bewilligte exakte SSID still erneut verbunden).
|
||||
/// [gatewayAddress] ist die Gateway-Adresse des Netzes, falls ermittelbar -
|
||||
/// dient als Vorbelegung der Zieladresse, bevor eine Gegenstelle aus dem
|
||||
/// Verkehr gelernt wurde (Doku Abschnitt 3).
|
||||
/// Verkehr gelernt wurde (Doku Abschnitt 3). [ssid] ist die tatsaechliche
|
||||
/// SSID des verbundenen Netzes, falls ermittelbar - wird gemerkt, damit
|
||||
/// kuenftige Verbindungen exakt (statt per Praefix) angefragt werden koennen
|
||||
/// (siehe [MlrsNetworkController.requestNetwork]).
|
||||
class MlrsNetworkAvailable extends MlrsNetworkEvent {
|
||||
const MlrsNetworkAvailable({this.gatewayAddress});
|
||||
const MlrsNetworkAvailable({this.gatewayAddress, this.ssid});
|
||||
|
||||
final String? gatewayAddress;
|
||||
final String? ssid;
|
||||
}
|
||||
|
||||
/// Die Anfrage konnte nicht erfuellt werden (Nutzer hat den Systemdialog
|
||||
@@ -79,9 +84,13 @@ abstract class MlrsNetworkController {
|
||||
Stream<MlrsNetworkEvent> get events;
|
||||
|
||||
/// Loest `ConnectivityManager.requestNetwork()` mit einem
|
||||
/// `WifiNetworkSpecifier` aus, dessen SSID-Muster auf [ssidPrefix] passt.
|
||||
/// `WifiNetworkSpecifier` aus. Ist [preferredSsid] gesetzt (zuvor
|
||||
/// erfolgreich verbundene, gemerkte SSID), wird exakt danach gefragt -
|
||||
/// Android kann das dann ohne erneuten Systemdialog automatisch bewilligen
|
||||
/// (Doku Abschnitt 8: "Verbindung automatisch herstellen"). Sonst wird
|
||||
/// [ssidPrefix] als Praefix-Muster verwendet (zeigt immer den Dialog).
|
||||
/// Das Ergebnis kommt asynchron ueber [events] (available/unavailable).
|
||||
Future<void> requestNetwork(String ssidPrefix);
|
||||
Future<void> requestNetwork(String ssidPrefix, {String? preferredSsid});
|
||||
|
||||
/// Meldet den Callback ab und geht damit von der aktuellen Anfrage
|
||||
/// zurueck - keine weiteren [events] danach, bis erneut angefragt wird.
|
||||
@@ -139,6 +148,7 @@ class MethodChannelMlrsNetworkController implements MlrsNetworkController {
|
||||
return switch (map['type']) {
|
||||
'available' => MlrsNetworkAvailable(
|
||||
gatewayAddress: map['gatewayAddress'] as String?,
|
||||
ssid: map['ssid'] as String?,
|
||||
),
|
||||
'lost' => const MlrsNetworkLost(),
|
||||
_ => const MlrsNetworkUnavailable(),
|
||||
@@ -155,8 +165,11 @@ class MethodChannelMlrsNetworkController implements MlrsNetworkController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestNetwork(String ssidPrefix) => _methodChannel
|
||||
.invokeMethod('requestNetwork', {'ssidPrefix': ssidPrefix});
|
||||
Future<void> requestNetwork(String ssidPrefix, {String? preferredSsid}) =>
|
||||
_methodChannel.invokeMethod('requestNetwork', {
|
||||
'ssidPrefix': ssidPrefix,
|
||||
'exactSsid': preferredSsid,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> releaseNetwork() => _methodChannel.invokeMethod('releaseNetwork');
|
||||
|
||||
@@ -32,16 +32,22 @@ class UdpTransport implements LinkTransport {
|
||||
required Future<String?> Function() getHost,
|
||||
required Future<int> Function() getPort,
|
||||
required Future<String> Function() getSsidPrefix,
|
||||
required Future<String?> Function() getRememberedSsid,
|
||||
required Future<void> Function(String ssid) rememberSsid,
|
||||
MlrsNetworkController? networkController,
|
||||
}) : _getHost = getHost,
|
||||
_getPort = getPort,
|
||||
_getSsidPrefix = getSsidPrefix,
|
||||
_getRememberedSsid = getRememberedSsid,
|
||||
_rememberSsid = rememberSsid,
|
||||
_networkController =
|
||||
networkController ?? MethodChannelMlrsNetworkController();
|
||||
|
||||
final Future<String?> Function() _getHost;
|
||||
final Future<int> Function() _getPort;
|
||||
final Future<String> Function() _getSsidPrefix;
|
||||
final Future<String?> Function() _getRememberedSsid;
|
||||
final Future<void> Function(String ssid) _rememberSsid;
|
||||
final MlrsNetworkController _networkController;
|
||||
|
||||
final _incomingController = StreamController<Uint8List>.broadcast();
|
||||
@@ -54,6 +60,7 @@ class UdpTransport implements LinkTransport {
|
||||
int? _boundPort;
|
||||
bool _socketOpen = false;
|
||||
bool? _staConcurrencySupported;
|
||||
String? _connectedSsid;
|
||||
|
||||
LinkState _state = LinkState.disconnected;
|
||||
LinkErrorReason? _lastErrorReason;
|
||||
@@ -85,6 +92,13 @@ class UdpTransport implements LinkTransport {
|
||||
/// nicht ermittelbar (API < 30, Doku Abschnitt 4/8).
|
||||
bool? get staConcurrencySupported => _staConcurrencySupported;
|
||||
|
||||
/// Tatsaechliche SSID des verbundenen Netzes, sobald von Android
|
||||
/// ermittelbar (Doku Abschnitt 8: "verbundenes WLAN anzeigen"). Wird bei
|
||||
/// jedem erfolgreichen [connect] auch dauerhaft gemerkt (siehe
|
||||
/// [_rememberSsid]), damit kuenftige Verbindungen exakt statt per Praefix
|
||||
/// angefragt werden koennen.
|
||||
String? get connectedSsid => _connectedSsid;
|
||||
|
||||
@override
|
||||
Future<void> connect() async {
|
||||
_lastErrorReason = null;
|
||||
@@ -92,13 +106,17 @@ class UdpTransport implements LinkTransport {
|
||||
_setState(LinkState.connecting);
|
||||
|
||||
final ssidPrefix = await _getSsidPrefix();
|
||||
final rememberedSsid = await _getRememberedSsid();
|
||||
final firstEvent = Completer<MlrsNetworkEvent>();
|
||||
final requestSub = _networkController.events.listen((event) {
|
||||
if (!firstEvent.isCompleted) firstEvent.complete(event);
|
||||
});
|
||||
|
||||
try {
|
||||
await _networkController.requestNetwork(ssidPrefix);
|
||||
await _networkController.requestNetwork(
|
||||
ssidPrefix,
|
||||
preferredSsid: rememberedSsid,
|
||||
);
|
||||
final event = await firstEvent.future;
|
||||
await requestSub.cancel();
|
||||
|
||||
@@ -115,6 +133,11 @@ class UdpTransport implements LinkTransport {
|
||||
// unten, nicht erst danach (Doku Abschnitt 8).
|
||||
_networkLossSub = _networkController.events.listen(_onNetworkEvent);
|
||||
|
||||
_connectedSsid = available.ssid;
|
||||
if (available.ssid != null) {
|
||||
await _rememberSsid(available.ssid!);
|
||||
}
|
||||
|
||||
final configuredHost = await _getHost();
|
||||
final configuredPort = await _getPort();
|
||||
final prefillHost = (configuredHost != null && configuredHost.isNotEmpty)
|
||||
@@ -170,6 +193,7 @@ class UdpTransport implements LinkTransport {
|
||||
_packetSub?.cancel();
|
||||
_packetSub = null;
|
||||
_boundPort = null;
|
||||
_connectedSsid = null;
|
||||
if (_socketOpen) {
|
||||
_socketOpen = false;
|
||||
unawaited(_networkController.closeSocket());
|
||||
@@ -193,6 +217,7 @@ class UdpTransport implements LinkTransport {
|
||||
await _packetSub?.cancel();
|
||||
_packetSub = null;
|
||||
_boundPort = null;
|
||||
_connectedSsid = null;
|
||||
if (_socketOpen) {
|
||||
_socketOpen = false;
|
||||
await _networkController.closeSocket();
|
||||
@@ -220,9 +245,14 @@ class UdpTransport implements LinkTransport {
|
||||
_packetSub?.cancel();
|
||||
if (_socketOpen) {
|
||||
_socketOpen = false;
|
||||
unawaited(_networkController.closeSocket());
|
||||
// dispose() darf nie eine unbehandelte Future-Exception hinterlassen -
|
||||
// z.B. wirft der Platform-Channel MissingPluginException, wenn
|
||||
// ueberhaupt kein natives Gegenstueck existiert (Tests, Web/Desktop-
|
||||
// UI-Vorschau). Der Aufrufer kann an dieser Stelle ohnehin nicht mehr
|
||||
// reagieren.
|
||||
unawaited(_networkController.closeSocket().catchError((_) {}));
|
||||
}
|
||||
unawaited(_networkController.releaseNetwork());
|
||||
unawaited(_networkController.releaseNetwork().catchError((_) {}));
|
||||
_networkController.dispose();
|
||||
_incomingController.close();
|
||||
_stateController.close();
|
||||
|
||||
@@ -9,6 +9,6 @@ abstract final class DmcColors {
|
||||
static const liveGreen = Color(0xFF3ECF8E);
|
||||
static const warnRed = Color(0xFFE2574A);
|
||||
static const pafAmber = Color(0xFFF0C86E);
|
||||
static const editModeGreen = Color(0xFF50C878);
|
||||
static const flyModeRed = Color(0xFFE2574A);
|
||||
static const planModeColor = Color(0xFFE2574A);
|
||||
static const flyModeColor = Color(0xFF50C878);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ final wifiTransportProvider = Provider<UdpTransport>((ref) {
|
||||
getHost: settingsRepo.loadConnectionWifiHost,
|
||||
getPort: settingsRepo.loadConnectionWifiPort,
|
||||
getSsidPrefix: settingsRepo.loadConnectionWifiSsidPrefix,
|
||||
getRememberedSsid: settingsRepo.loadConnectionWifiLastSsid,
|
||||
rememberSsid: settingsRepo.setConnectionWifiLastSsid,
|
||||
);
|
||||
ref.onDispose(transport.dispose);
|
||||
return transport;
|
||||
@@ -134,9 +136,10 @@ class WifiConnectionLogNotifier
|
||||
final message = switch (linkState) {
|
||||
LinkState.disconnected => 'Disconnected',
|
||||
LinkState.connecting => 'Requesting mLRS network...',
|
||||
LinkState.connected =>
|
||||
'Network available, listening on port ${transport.boundPort}'
|
||||
' (STA concurrency: ${_formatStaConcurrency(transport.staConcurrencySupported)})',
|
||||
LinkState.connected => 'Connected to '
|
||||
'${transport.connectedSsid ?? 'network'}, listening on port '
|
||||
'${transport.boundPort} (STA concurrency: '
|
||||
'${_formatStaConcurrency(transport.staConcurrencySupported)})',
|
||||
LinkState.error => 'Error: '
|
||||
'${transport.lastErrorMessage ?? transport.lastErrorReason?.name ?? 'unknown'}',
|
||||
};
|
||||
|
||||
@@ -150,6 +150,12 @@ class _WifiPanelState extends ConsumerState<_WifiPanel> {
|
||||
final connectionSettings = ref.watch(connectionSettingsProvider).value;
|
||||
final log = ref.watch(wifiConnectionLogProvider);
|
||||
final packetCount = ref.watch(wifiConnectionLogProvider.notifier).packetCount;
|
||||
// Kein eigener Reaktivitaets-Provider fuer connectedSsid noetig - der
|
||||
// Rebuild wird bereits durch das Beobachten von wifiLinkStateProvider
|
||||
// oben ausgeloest, und das Feld wird synchron gesetzt, bevor der
|
||||
// Transport auf LinkState.connected wechselt (siehe UdpTransport.connect).
|
||||
final connectedSsid = ref.read(wifiTransportProvider).connectedSsid;
|
||||
final rememberedSsid = connectionSettings?.connectionWifiLastSsid;
|
||||
|
||||
// Controller einmalig aus den gespeicherten Einstellungen befuellen -
|
||||
// nicht bei jedem Rebuild, sonst wird waehrend der Nutzereingabe
|
||||
@@ -188,6 +194,23 @@ class _WifiPanelState extends ConsumerState<_WifiPanel> {
|
||||
'break maps/weather.',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (connectedSsid != null)
|
||||
Text(
|
||||
'Connected to: $connectedSsid',
|
||||
style: const TextStyle(fontSize: 12, color: DmcColors.liveGreen),
|
||||
)
|
||||
else if (rememberedSsid != null)
|
||||
Text(
|
||||
'Remembered network: $rememberedSsid',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
)
|
||||
else
|
||||
const Text(
|
||||
'No network remembered yet - connect once to remember it for '
|
||||
'automatic reconnection.',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
if (errorReason != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
_WifiErrorHint(reason: errorReason),
|
||||
@@ -253,6 +276,22 @@ class _WifiPanelState extends ConsumerState<_WifiPanel> {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Auto-connect when entering Fly mode'),
|
||||
subtitle: const Text(
|
||||
'Only once a network has been remembered (see above).',
|
||||
style: TextStyle(fontSize: 11),
|
||||
),
|
||||
value: connectionSettings?.connectionAutoConnect ?? true,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(appSettingsRepositoryProvider)
|
||||
.setConnectionAutoConnect(value);
|
||||
ref.invalidate(connectionSettingsProvider);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Connection log ($packetCount packets received)',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -5,8 +6,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app_mode/app_mode_cubit.dart';
|
||||
import '../../transport/connection_type.dart';
|
||||
import '../../transport/link_transport.dart';
|
||||
import '../dmc_colors.dart';
|
||||
import '../providers/connection_settings_provider.dart';
|
||||
import '../providers/flight_mode_provider.dart';
|
||||
import '../providers/wifi_connection_provider.dart';
|
||||
import 'flight_mode_pill.dart';
|
||||
import 'fly_map_controls.dart';
|
||||
import 'header_wind_pill.dart';
|
||||
@@ -35,7 +40,7 @@ class TopModeBar extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isFly = context.select((AppModeCubit c) => c.state.mode == AppMode.fly);
|
||||
final activeTint = isFly ? DmcColors.flyModeRed : DmcColors.editModeGreen;
|
||||
final activeTint = isFly ? DmcColors.flyModeColor : DmcColors.planModeColor;
|
||||
|
||||
// Kein SafeArea: das wendet links/rechts die jeweils tatsaechlichen
|
||||
// (oft asymmetrischen) Insets an, wodurch die Leiste sichtbar von der
|
||||
@@ -72,7 +77,7 @@ class TopModeBar extends ConsumerWidget {
|
||||
_ModeButton(
|
||||
label: 'Plan',
|
||||
isActive: !isFly,
|
||||
inactiveColor: DmcColors.editModeGreen,
|
||||
inactiveColor: DmcColors.planModeColor,
|
||||
cornerRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(26),
|
||||
),
|
||||
@@ -82,6 +87,8 @@ class TopModeBar extends ConsumerWidget {
|
||||
const SizedBox(width: 8),
|
||||
const HeaderWindPill(),
|
||||
const SizedBox(width: 8),
|
||||
const _WifiConnectionProgressPill(),
|
||||
const SizedBox(width: 8),
|
||||
const Expanded(child: FlightModePill()),
|
||||
const SizedBox(width: 8),
|
||||
const FlyMapControls(),
|
||||
@@ -96,7 +103,7 @@ class TopModeBar extends ConsumerWidget {
|
||||
_ModeButton(
|
||||
label: 'Fly',
|
||||
isActive: isFly,
|
||||
inactiveColor: DmcColors.flyModeRed,
|
||||
inactiveColor: DmcColors.flyModeColor,
|
||||
cornerRadius: const BorderRadius.only(
|
||||
bottomRight: Radius.circular(26),
|
||||
),
|
||||
@@ -105,6 +112,7 @@ class TopModeBar extends ConsumerWidget {
|
||||
// Eintritt in den Fly-Modus setzt immer auf Waypoint
|
||||
// zurueck (Doku 3.11).
|
||||
ref.read(flightModeProvider.notifier).reset();
|
||||
_maybeAutoConnectWifi(ref);
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -115,6 +123,86 @@ class TopModeBar extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Verbindet automatisch zur mLRS-Bruecke, wenn in den Fly-Modus gewechselt
|
||||
/// wird (Doku Kommunikationsschicht v2 Abschnitt 8: "Verbindung automatisch
|
||||
/// herstellen bei Wechsel in Fly, wenn WLAN gemerkt"). Voraussetzungen:
|
||||
/// Verbindungsart ist WLAN, "Auto-connect"-Einstellung ist an, und es gibt
|
||||
/// bereits eine gemerkte SSID aus einer frueheren erfolgreichen Verbindung -
|
||||
/// ohne gemerkte SSID muesste ohnehin der Systemdialog erscheinen, das ist
|
||||
/// dann kein "automatisches" Verbinden mehr. Der Fortschritt ist ueber
|
||||
/// [_WifiConnectionProgressPill] sichtbar.
|
||||
Future<void> _maybeAutoConnectWifi(WidgetRef ref) async {
|
||||
final settings = ref.read(connectionSettingsProvider).value;
|
||||
if (settings == null) return;
|
||||
if (settings.connectionType != ConnectionType.wifi) return;
|
||||
if (!settings.connectionAutoConnect) return;
|
||||
if (settings.connectionWifiLastSsid == null) return;
|
||||
|
||||
final transport = ref.read(wifiTransportProvider);
|
||||
if (transport.currentState == LinkState.connected ||
|
||||
transport.currentState == LinkState.connecting) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await transport.connect();
|
||||
} on LinkConnectException {
|
||||
// Grund/Nachricht sind bereits ueber den State-Stream gemeldet, siehe
|
||||
// _WifiConnectionProgressPill.
|
||||
}
|
||||
}
|
||||
|
||||
/// Kompakte Fortschrittsanzeige fuer den automatischen WLAN-Verbindungsaufbau
|
||||
/// (siehe [_maybeAutoConnectWifi]) - nur sichtbar, solange die Verbindungsart
|
||||
/// WLAN ist und gerade etwas passiert bzw. ein Fehler vorliegt. Bleibt im
|
||||
/// Ruhezustand (disconnected, kein vorheriger Fehler) unsichtbar, damit die
|
||||
/// Kopfleiste nicht dauerhaft mit einer Statuspille zugestellt ist.
|
||||
class _WifiConnectionProgressPill extends ConsumerWidget {
|
||||
const _WifiConnectionProgressPill();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final connectionType = ref.watch(connectionSettingsProvider).value?.connectionType;
|
||||
final linkState = ref.watch(wifiLinkStateProvider).value;
|
||||
if (connectionType != ConnectionType.wifi) return const SizedBox.shrink();
|
||||
if (linkState == null || linkState == LinkState.disconnected) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final (label, color) = switch (linkState) {
|
||||
LinkState.connecting => ('Connecting…', DmcColors.pafAmber),
|
||||
LinkState.connected => ('WiFi connected', DmcColors.liveGreen),
|
||||
LinkState.error => ('WiFi error', DmcColors.warnRed),
|
||||
LinkState.disconnected => ('', Colors.grey),
|
||||
};
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: color),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (linkState == LinkState.connecting) ...[
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: CircularProgressIndicator(strokeWidth: 1.5, color: color),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
],
|
||||
Text(label, style: TextStyle(color: color, fontSize: 11)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ModeButton extends StatelessWidget {
|
||||
const _ModeButton({
|
||||
required this.label,
|
||||
|
||||
Reference in New Issue
Block a user