fix(learn): nothing of Learn's goes to Android's backup or to another phone (ADR 0019, D9)
Some checks failed
CI / check (push) Has been cancelled
CI / deploy (push) Has been cancelled

The device keeps private studysets open in its database, which Android's
backup (Google's, or Seedvault where turned on) and a transfer to a new
phone took open. allowBackup and fullBackupContent are off, and from
Android 12 on the data extraction rules exclude every folder from the
transfer too, which allowBackup alone no longer stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-30 16:21:52 +01:00
commit 0ec15d2fb2
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,59 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
/// Nothing of Learn's leaves the phone by Android's backup or a transfer to
/// another phone (ADR 0019, D9): what the device keeps of a private
/// studyset is open in its database, and would go there open; what is
/// sealed comes back from the instance anyway.
void main() {
final manifest = File(
'android/app/src/main/AndroidManifest.xml',
).readAsStringSync();
final application = RegExp(
'<application[^>]*>',
).firstMatch(manifest)!.group(0)!;
test('backup is off, through Android 11 as from 12 on', () {
expect(application, contains('android:allowBackup="false"'));
expect(application, contains('android:fullBackupContent="false"'));
expect(
application,
contains('android:dataExtractionRules="@xml/data_extraction_rules"'),
);
});
test('from Android 12 on, neither a backup nor a transfer takes any of the '
"app's folders", () {
// allowBackup="false" leaves a transfer to another phone on from
// Android 12 on: the rules have to exclude it too, folder by folder.
final rules = File(
'android/app/src/main/res/xml/data_extraction_rules.xml',
).readAsStringSync();
for (final kind in ['cloud-backup', 'device-transfer']) {
final section = RegExp(
'<$kind>(.*?)</$kind>',
dotAll: true,
).firstMatch(rules)?.group(1);
expect(section, isNotNull, reason: '$kind is missing');
final excluded = {
for (final match in RegExp(
r'<exclude domain="(\w+)" path="\."\s*/>',
).allMatches(section!))
match.group(1),
};
expect(excluded, {
'root',
'file',
'database',
'sharedpref',
'external',
'device_root',
'device_file',
'device_database',
'device_sharedpref',
}, reason: '$kind excludes');
expect(section, isNot(contains('<include')), reason: kind);
}
});
}